2 The Eight Myths of Erlang Performance

Some truths seem to live on well beyond their best-before date,
perhaps because "information" spreads more rapidly from person-to-person
faster than a single release note that notes, for instance, that funs
have become faster.

Here we try to kill the old truths (or semi-truths) that have
become myths.

Yes, funs used to be slow. Very slow. Slower than apply/3.
Originally, funs were implemented using nothing more than
compiler trickery, ordinary tuples, apply/3, and a great
deal of ingenuity.

But that is ancient history. Funs was given its own data type
in the R6B release and was further optimized in the R7B release.
Now the cost for a fun call falls roughly between the cost for a call to
local function and apply/3.

List comprehensions used to be implemented using funs, and in the
bad old days funs were really slow.

Nowadays the compiler rewrites list comprehensions into an ordinary
recursive function. Of course, using a tail-recursive function with
a reverse at the end would be still faster. Or would it?
That leads us to the next myth.

According to the myth,
recursive functions leave references
to dead terms on the stack and the garbage collector will have to
copy all those dead terms, while tail-recursive functions immediately
discard those terms.

That used to be true before R7B. In R7B, the compiler started
to generate code that overwrites references to terms that will never
be used with an empty list, so that the garbage collector would not
keep dead values any longer than necessary.

Even after that optimization, a tail-recursive function would
still most of the time be faster than a body-recursive function. Why?

It has to do with how many words of stack that are used in each
recursive call. In most cases, a recursive function would use more words
on the stack for each recursion than the number of words a tail-recursive
would allocate on the heap. Since more memory is used, the garbage
collector will be invoked more frequently, and it will have more work traversing
the stack.

In R12B and later releases, there is an optimization that will
in many cases reduces the number of words used on the stack in
body-recursive calls, so that a body-recursive list function and
tail-recursive function that calls lists:reverse/1 at the
end will use exactly the same amount of memory.
lists:map/2, lists:filter/2, list comprehensions,
and many other recursive functions now use the same amount of space
as their tail-recursive equivalents.

So which is faster?

It depends. On Solaris/Sparc, the body-recursive function seems to
be slightly faster, even for lists with very many elements. On the x86
architecture, tail-recursion was up to about 30 percent faster.

So the choice is now mostly a matter of taste. If you really do need
the utmost speed, you must measure. You can no longer be
absolutely sure that the tail-recursive list function will be the fastest
in all circumstances.

Note: A tail-recursive function that does not need to reverse the
list at the end is, of course, faster than a body-recursive function,
as are tail-recursive functions that do not construct any terms at all
(for instance, a function that sums all integers in a list).

Actually, string handling could be slow if done improperly.
In Erlang, you'll have to think a little more about how the strings
are used and choose an appropriate representation and use
the re module instead of the obsolete
regexp module if you are going to use regular expressions.

BEAM is a register-based virtual machine. It has 1024 virtual registers
that are used for holding temporary values and for passing arguments when
calling functions. Variables that need to survive a function call are saved
to the stack.

BEAM is a threaded-code interpreter. Each instruction is word pointing
directly to executable C-code, making instruction dispatching very fast.