Erlang solution for excercise 8.11 in Programming Erlang

In my previous post, I presented a Java solution to the Ring problem. Following is my Erlang solution.

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.

One Comment

Leave a Reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>