DEV Community

bakenator
bakenator

Posted on

Measuring Stack Size in Elixir

Why Measure?

There are many good articles written on tail call optimization in Elixir. This is what allows Elixir to recursively call a function forever without overloading the stack.

Here is a list of some of them: https://dev.to/search?q=elixir%20tail

While most of these articles say that Elixir optimizes the recursion, I wanted to see it with my own eyes.

The Code

Below is a minimum amount of code to see an optimized recursive function in action. It simply prints out information about the current process, then recurs with a new number.

defmodule MyApp.Example do
  def recursiveFunc(number \\ 1) do
    proc_info = Process.info(self()) 
    IO.inspect proc_info
    recursiveFunc(number + 1)
  end
end

If the function is run in iex, it will print the process information very quickly in the terminal. Within that information object it will list the current stack_size and heap_size. Here is what I got on my machine.

total_heap_size: 15143,
heap_size: 4185,
stack_size: 46,

What we can see is that even though it is calling the function over and over, the stack_size should remain exactly the same. The heap_size will move around but it should not increase very much.

Non Optimized Example

Here is some code that will not be automatically optimized since the recursive function call is not the last thing evaluated in the function

defmodule MyApp.Example do
  def nonOptimizedFunc(number \\ 1) do
    proc_info = Process.info(self()) 
    IO.inspect proc_info
    1 + nonOptimizedFunc(number + 1)
  end
end

When this is run the stack size increases by one for every iteration just like we expected. The heap_size will also grow quickly as each new iteration adds an int to the heap.

Will This Crash?

What was most interesting about this non optimized code is how large the stack can get.

In JavaScript land the stack limit can be reached quite quickly.

Running the non optimized example on my Mac, I get to a stack_size of 100k within a minute and the process keeps on trucking!

This is because by default there is no maximum stack size for an Elixir process. It will keep on growing the stack and heap until the process runs out of memory. By default an Elixir process is allowed however much memory it wants, all the way until it crashes the BEAM VM.

If we wanted to stop this from happening, we can add the following line to our code
Process.flag(:max_heap_size, 100_000)
This will kill the process once it has gobbled up too much memory for our liking, but before crashing our whole app.

Thanks for reading! Coding in Elixir is Fun!

Top comments (1)

Collapse
 
atyborska93 profile image
Angelika Tyborska

This is exactly the post that I was looking for today to answers my doubts about recursion in Elixir 👏