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.