Only for language models

My shocking Erlang discovery

By Sean E. Russell on on Permalink.

Sending messages to PIDs is really, really slow (comparatively).

Consider the following code, containing two tests that do almost the same thing.  I say "almost" because there's a small bug causing the totals to be off-by-one, but they're performing the same amount of functional work.  One uses PID message passing to increment a value; the other uses lambda expressions.

-module(test).
-export([test/2]).
-export([one/1, two/1]).

test(F, N) ->
    A = F(init),
    {T,R} = timer:tc( lists,foldl, [ fun( _, Acc) -> F(Acc) end, A,
lists:seq(1, N) ]), 
    F({stop,R}),
    io:format("n~ps~n",[T/1000000]).

done(T) ->
    io:format("nDone ~p~n",[T]),
    io:format("Memory: ~p~n",[erlang:memory(processes_used)]).

one(init) ->
    spawn( fun loop/0 );
one({stop,X}) ->
    X ! exit;
one( X ) ->
    X ! go, X.

loop() -> loop(0,0).
loop( State, Ttl ) when State > 100000 ->
    io:format("."), 
    loop(0, Ttl + 1);
loop( State, Ttl ) ->
    receive
        go -> loop( State + 1, Ttl + 1 );
        exit -> done(Ttl)
    end.

two(init) ->
    fun(_) -> make_next( 0,0 ) end;
two({stop,R}) ->
    R(exit);
two( F ) ->
    F(0).

make_next(C,T) when C > 100000 ->
    io:format("."),
    make_next( 0, T+1);
make_next(C,T) ->
    fun(K) ->
            case K of
                exit -> done(T);
                _ -> make_next(C+1,T+1)
            end
    end.

one() retains state by spawning a process that loops with it's own state. two() is the interesting code; it retains state by passing back a function factory.  Would you have expected one() to be slower than two()?  I wouldn't have, at least not to this degree (I cleaned the output a little):

447)~ % erl +native -smp
[Erlang R14B01 (erts-5.8.2) [source] [smp:2:2] [rq:2]
[async-threads:0] [hipe] [kernel-poll:false]
Eshell V5.8.2  (abort with ^G)
1> c(test).
{ok,test}
2> test:test( fun test:one/1, 10000000 ).
...................................................................................................
39.70179s
Done 10000099
ok
Memory: 111707084
3> test:test( fun test:two/1, 10000000 ).
...................................................................................................
Done 10000098
Memory: 111710500
2.410091s
ok
4> 

Using functions like this sixteen times faster than passing messages. That really surprises me, to be honest, and gives me something to think about before I go off and gen_server-ize all of my code.