Revisiting Erlang

This year's holiday project is to revisit my attempt to learn Erlang, this time in a more structured fashion, following Joe Armstrong's book.

The first substantive exercise is:

Write a ring benchmark. Create N processes in a ring. Send a message round the ring M times so that a total of N * M messages get sent. Time how long this takes for different values of N and M.

Write a similar program in some other programming language you are familiar with. Compare the results. Write a blog, and publish the results on the internet!

Programming Erlang
— Joe Armstrong

No benchmarking or comparison, I'm afraid, but here's my solution.

ringfun(_, 0) ->
    ok;
ringfun(Pid, Times) ->
    receive
        next ->
            io:format("Pid ~p sending message ~p~n", [self(), Times]),
            Pid ! next,
            ringfun(Pid, Times-1);
        Pid2 ->
            ringfun(Pid2, Times)
    end.

ring(N, M) when N>1, M>=1 ->
    FirstPid = spawn(fun() -> ringfun(ok, M) end),
    FirstPid ! lists:foldl(fun(_, Pid) ->
                                   spawn(fun() -> ringfun(Pid, M) end)
                           end, FirstPid,
                           lists:seq(1, N-1)),
    FirstPid ! next,
    ok.

The version above had no timing information. It also didn't communicate back to the parent process that the messages had finished circulating. These two issues were related (since the parent process needs to know when to stop timing. And the final time needs to be calculated on the final process in case the child processes have a different notion of time.) So the updated version below fixes both these issues:

ringfun(_, 0, ok) ->
    ok;
ringfun(_, 0, ParentPid) ->
    ParentPid ! done;
ringfun(NextPid, Times, ParentPid) ->
    receive
        next ->
            NextPid ! next,
            ringfun(NextPid, Times-1, ParentPid);
        NewPid ->
            ringfun(NewPid, Times, ParentPid)
    end.

ring(N, M) when N>1, M>0 ->
    S = self(),
    FirstPid = spawn(fun() -> ringfun(ok, M, S) end),
    FirstPid ! lists:foldl(fun(_, Pid) ->
                                   spawn(fun() -> ringfun(Pid, M, ok) end)
                           end, FirstPid,
                           lists:seq(1, N-1)),
    T1 = erlang:now(),
    FirstPid ! next,
    receive
        done ->
            T2 = erlang:now()
    end,
    Tdiff = timer:now_diff(T2, T1)/1000000,
    io:format("Sent ~p messages around a ring of length ~p in ~p seconds~n",
              [M, N, Tdiff]).

and a quick proof from the pudding:

13> chapter8:ring(3000,3000).
Sent 3000 messages around a ring of length 3000 in 10.3243 seconds
ok
Comments have been automatically disabled | digg this | reddit | del.icio.us