I’ve been reading Joe Armstrong’s excellent Programming Erlang. In it he proposes an exercise:

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.

I found the exercise very instructive. My solution follows.

-module(ring).
-export([start/2, foo/1, bar/1, timer/0]).

%%----------------------------------------------------
%% Create N processes in a ring.
%% Send a message round the ring M times,
%% so that N * M messages get sent.
%% Time performance for different values of N and M.
%%----------------------------------------------------

foo(Next) ->
    receive
	{0, Any} ->
	    Next ! {0, Any};	%% send the 0 to tell everyone to shut down
	{N, Any} ->
	    Next ! {N, Any},
	    foo(Next)
    end.

bar(Timer) ->
    receive
	{0, Next} ->
	    Timer ! done,
	    Next ! {0, Next};
	{N, Next} ->
	    Next ! {N-1, Next},
	    bar(Timer)
    end.

timer() ->
    statistics(runtime),
    statistics(wall_clock),
    receive
	done ->
	    {_, RT} = statistics(runtime),
	    {_, WC} = statistics(wall_clock),
	    io:format("Total time=~p (~p) milliseconds~n", [RT, WC])
    end.

%%----------------------------------------------------
%% construct a chain of processes
%% process J-1 routes msgs to process J for all J < K
%%----------------------------------------------------
chain(0, Pid) -> Pid;
chain(K, Pid) ->
     chain(K-1, spawn(ring,foo,[Pid])).

%%----------------------------------------------------
%% N is the number of processes in the ring
%% M is the number of times msgs circle the ring
%%----------------------------------------------------
start(N, M) ->
    %%  N-1 processes are of type foo, one is of type bar
    A= spawn(ring, bar, [spawn(ring, timer,[])]),
    B= chain(N-1, A),
    A ! {M, B}.