PragTob

PragTob

Tail Call Optimization in Elixir & Erlang – not as efficient and important as you probably think

I wrote up a detailed blog post about tail call optimization in Elixir/Erlang and its performance. The TLDR; sort of is that none tail call optimized recursive functions (body-recursive) can be faster and more memory efficient than TCO functions. This is something that I never thought before, that TCO is always faster seems to be a common misconception. My example is also not contrived - it’s an implementation of map.

Posting here to share as I think it’s important knowledge :slight_smile: Also to get feedback, maybe someone has another implementation that blows mine out of the water.

PS: Hope this is the appropriate forum/tag?

Most Liked

rvirding

rvirding

Creator of Erlang

Sorry, there are some cases where TCO functions are absolutely critical. Kill your system and make it worthless if you don’t do it critical. You just have to get it right.

The examples you show are all calls to functions which (eventually) return and in those cases TCO is a nice but not need. It may not save you heap but it will save you stack, which can actually save you GC as well[*].

No, the main case where is TCO is critical is in process top-loops. These functions never return (unless the process dies) so they will build up stack never to release it. Here you have to get it right. There are no alternatives. The same applies if you top-loop is actually composed of a set of mutually calling functions. There there are no alternatives. Sorry for pushing this again, and again, but it is critical. :slight_smile:

In other cases my way of choosing is the one which gives me the most intelligible code. And that varies depending on what the function is supposed to do and how I choose to do it.

Robert

[*] This is because the heap and stack share the same memory area and when it is full you get a gc. So keeping the stack small can delay gc.

17
Post #3
PragTob

PragTob

And more resurrection, I used benchee 1.0 to take this (as one of the “original” benchee benchmarks) up for a spin to see how things have improved and found some peculiar behaviour: Revisiting “Tail Call Optimization in Elixir & Erlang” with benchee 1.0 – Journeys of a not so young anymore Software Engineer

PragTob

PragTob

I just updated the post after all this time (somebody commented) - when the list size is increased it seems like tail-recursive functions get faster again. E.g. instead of the original 10_000 element list I used a 10_000_000 list and recorded the benchmarking results here. The tail-recursive functions are almost twice as fast as the body-recursive version and the standard library version.

So, always benchmark your own use case and it’s time that I give benchee its multiple inputs feature :smiley:

(sorry for doing a little resurrection but I thought this was a rather important point to raise here)

Where Next?

Popular in Discussions Top

Rustixir
Hi everyone, im working on find best language/framework/system for high concurrency, high performance and stable performance after wor...
New
crispinb
On reading dhh’s latest The One Person Framework it strikes me that Phoenix with LiveView is already pretty much this. However, never hav...
New
lucaong
Hello Elixir and Nerves community, I have been working for a while on an open-source embedded key-value database for Elixir, that I call...
230 14049 124
New
Crowdhailer
I’ve been hearing much about the new formatter and it’s something I have been keen to try. I find examples buy far the most illuminating...
248 19365 150
New
CharlesO
Erlang :list.nth simple, but 1 - based nth(1, [H|_]) -> H; nth(N, [_|T]) when N > 1 -> nth(N - 1, T). Elixir Enum.at … coo...
New
saif
Hello everyone, Long time lurker first time poster here. I’ve recently begun working on Elixir full-time again! :raised_hands: It’s been...
New
New
eteeselink
Hi all, In the last days, two things happened: A blog post titled “They might never tell you it’s broken” made the rounds. It’s about ...
New
AstonJ
Can you believe the first professionally published Elixir book was published just 8 years ago? Since then I think we’ve seen more books f...
New
Qqwy
Looking at the stacks that existing large companies have used, WhatsApp internally uses Mnesia to store the messages, while Discord uses ...
New

Other popular topics Top

marius95
Hello everyone, I try to use an Javascript Event Handler in my root.html.leex file. Therefore I created a function in the app.js file: ...
New
mcarvalho
What is the difference between System.get_env and Application.get_env? For example, what are best practices to use one versus another.
New
skosch
To my knowledge, put_in, Map.update etc. all have the one limitation of not automatically creating intermediate keys when needed (for exa...
New
Patoshizzle
After calling mix ecto.create I get this error: 17:00:32.162 [error] GenServer #PID<0.412.0> terminating ** (Postgrex.Error) FATAL...
New
shahryarjb
Hello, I have map which I want to convert it to string like this: the map: %{last_name: "tavakkoli", name: "shahryar"} the string I ne...
New
saif
Hello everyone, Long time lurker first time poster here. I’ve recently begun working on Elixir full-time again! :raised_hands: It’s been...
New
shijith.k
I am trying to start a new phoenix project with elixir 1.9, but mix phx.new does not work. It says that ** (Mix) The task "phx.new" could...
New
joaquinalcerro
Hi there, I am working with Ecto-Postgresql and I need to call all of the records from a specific table but the table has 40,000 records...
New
JorisKok
I have a server on AWS, and was running a load test using artillery. When looking at the Phoenix dashboard I see the Ports going to 100% ...
New
jononomo
For some reason my phoenix channels are working for me in my local dev environment, but as soon as I deploy via Docker, I get a 403 error...
New

We're in Beta

About us Mission Statement