In my previous post, I presented a Java solution to the Ring problem. Following is my Erlang solution.
-module(ring_benchmark).
-export([start/2, ring_starter/1, ring_member/1, count_down/0]).
start(N, M) ->
statistics(runtime),
statistics(wall_clock),
CountDown = spawn(?MODULE, count_down, []),
First = spawn(?MODULE, ring_starter, [CountDown]),
Last = init_ring(N-1, First),
{_, Spawn_RT} = statistics(runtime),
{_, Spawn_WC} = statistics(wall_clock),
io:format("Spawn time=~p (~p) milliseconds~n" , [Spawn_RT, Spawn_WC]),
Messages = create_messages(M),
CountDown ! {start},
[First ! {pass, Last, AMessage} || AMessage <- Messages],
ok.
init_ring(0, Neighbor) ->
Neighbor;
init_ring(N, Neighbor) ->
Next = spawn(?MODULE, ring_member, [Neighbor]),
init_ring(N-1, Next).
%% Creates M messages. The last message is {quit}
create_messages(1) ->
[quit];
create_messages(M) ->
[M | create_messages(M - 1)].
%% First member in the ring
ring_starter(CountDown) ->
receive
{quit} ->
CountDown ! {stop};
{Message} ->
ring_starter(CountDown);
{pass, To, Message} ->
To ! {Message},
ring_starter(CountDown)
end.
%% Represents all other members in a ring
ring_member(Neighbor) ->
receive
{quit} ->
Neighbor ! {quit};
{Message} ->
Neighbor ! {Message},
ring_member(Neighbor)
end.
count_down() ->
receive
{start} ->
statistics(runtime),
statistics(wall_clock),
count_down();
{stop} ->
{_, PassEnd_RT} = statistics(runtime),
{_, PassEnd_WC} = statistics(wall_clock),
io:format("Message passing time=~p (~p) milliseconds~n" , [PassEnd_RT, PassEnd_WC])
end.
The starting point of the program is start(N, M). A ring of N members is constructed with init_ring() functions. The Pid of each member is passed to the next member during creation of ring_member. The first member of the ring is ring_starter(). It starts passing messages by sending message to the last member and waits for the same message to come back to it. The last message is {quit}. When the last message comes back to the ring_starter() the program ends. count_down() is used to track the time taken.
I’ve stripped out io:format() statements, which are like debug statements. They show messages being passed around in the ring. The same program with io:format() statements is given below:
-module(ring).
-export([start/2, ring_starter/1, ring_member/1, count_down/0]).
start(N, M) ->
statistics(runtime),
statistics(wall_clock),
CountDown = spawn(?MODULE, count_down, []),
First = spawn(?MODULE, ring_starter, [CountDown]),
Last = init_ring(N-1, First),
{_, Spawn_RT} = statistics(runtime),
{_, Spawn_WC} = statistics(wall_clock),
io:format("Spawn time=~p (~p) milliseconds~n" , [Spawn_RT, Spawn_WC]),
Messages = create_messages(M),
CountDown ! {start},
[First ! {pass, Last, AMessage} || AMessage <- Messages],
ok.
init_ring(0, Neighbor) ->
Neighbor;
init_ring(N, Neighbor) ->
Next = spawn(?MODULE, ring_member, [Neighbor]),
init_ring(N-1, Next).
%% Creates M messages. The last message is {quit}
create_messages(1) ->
[quit];
create_messages(M) ->
[M | create_messages(M - 1)].
%% First member in the ring
ring_starter(CountDown) ->
receive
{quit} ->
CountDown ! {stop},
io:format("~w received ~w~n", [self(), quit]);
{Message} ->
io:format("~w received ~w~n", [self(), Message]),
ring_starter(CountDown);
{pass, To, Message} ->
To ! {Message},
io:format("~w sent ~w to ~w~n", [self(), Message, To]),
ring_starter(CountDown)
end.
%% Represents all other members in a ring
ring_member(Neighbor) ->
receive
{quit} ->
Neighbor ! {quit},
io:format("~w sent ~w to ~w~n", [self(), quit, Neighbor]);
{Message} ->
Neighbor ! {Message},
io:format("~w sent ~w to ~w~n", [self(), Message, Neighbor]),
ring_member(Neighbor)
end.
count_down() ->
receive
{start} ->
statistics(runtime),
statistics(wall_clock),
count_down();
{stop} ->
{_, PassEnd_RT} = statistics(runtime),
{_, PassEnd_WC} = statistics(wall_clock),
io:format("Message passing time=~p (~p) milliseconds~n" , [PassEnd_RT, PassEnd_WC])
end.
I construct a list of M messages and pass each message at a time. This line does exactly that:
[First ! {pass, Last, AMessage} || AMessage <- Messages]
This is equivalent to the call: pass_messages(First, Last, Messages)
pass_messages(First, Last, [Message | []]) ->
First ! {pass, Last, Message};
pass_messages(First, Last, [Message | Rest]) ->
First ! {pass, Last, Message},
pass_messages(First, Last, Rest).
In my next post, I’ll show message processing times for different values of N and M in Java 6 and Erlang.
Pingback: Erlang vs. Java message passing times | Vijay Kandy