?

Log in

No account? Create an account

Previous Entry | Next Entry

an improved ring benchmark

This one cleans up after itself by using linked processes in the process ring. This means that after the benchmark is run, all the processes in the ring are killed and the memory is freed, hopefully avoiding that nasty memory leak that locked up my workstation earlier today.

an improved answer to joe armstrong's programming erlang chapter 8 problem 2 under the cut.

ETA: I think there may be one more bug here - the tail process might not be properly linked to the other processes in the ring. I'm not sure if it's actually being killed when the kill message goes around... will need to do more testing to be sure.


-module(proc_ring).
-export([
runlist/2,
run/1,
start/2,
clock/1,
loop/1,
tail_loop/3,
for/3,
head/1
]).

%% spawn a tail process.
%% spawn Procs-1 body processes in an array.
%% link each process in the array to the
%% process.
%% link the last one to the head.
%% link the head to the first process

%% send the message around from the head.
%% clock it each time it passes the head.




runlist({MaxProc,Pstep},{MaxMsg,Mstep})->
[run([A,B])||A<-lists:seq(1,MaxProc,Pstep),
B<-lists:seq(1,MaxMsg,Mstep)];
runlist(MaxProc,MaxMsg)->
[run([A,B])||A<-lists:seq(1,MaxProc),
B<-lists:seq(1,MaxMsg)].

run([Procs,Messages])->
io:format("Procs ~p, Messages ~p ",[Procs,Messages]),
L = start(Procs,Messages),
H = head(L),
{U1,U2} = clock(H),
Tmsg = Procs*Messages,
T1 = U1/Tmsg,
T2 = U2/Tmsg,
io:format("per message time ~p uS (~p uS)~n",[T1,T2]).

start(Procs,Messages)->
process_flag(trap_exit,true),
Tail = spawn_link(proc_ring,tail_loop,[1,Messages,fun()->a end]),
List = [Tail|spawnlist(2,Procs, Tail)],
Rev = lists:reverse(List),
[Head|_]=Rev,
sender(Tail,{update,Head}),
List.

head(Proclist)->
[H|_]=lists:reverse(Proclist),
H.

clock(Pid)->
% io:format("starting ring benchmark~n"),
statistics(runtime),
statistics(wall_clock),
Pid!{self(),transmit},
receive
{done}->
{_,Time1}=statistics(runtime),
{_,Time2}=statistics(wall_clock),
Pid!{die},
U1 = Time1 * 1000,
U2 = Time2 * 1000
%io:format("CPU ~p uS, wall ~p uS~n",
% [U1,U2])
end,
{U1,U2}.



spawnlist(I,N,PrevPid) when I>=N->
[init(PrevPid)];
spawnlist(I,N,PrevPid) ->
ThisPid = init(PrevPid),
[ThisPid|spawnlist(I+1,N,ThisPid)].



for(I,N,Fun) when I > N->
[Fun()];
for(I,N,Fun) ->
[Fun()|for(I+1,N,Fun)].

init(Pid)->
Me = spawn_link(proc_ring,loop,[Pid]),
% io:format("I, ~p, have the target ~p~n",[Me,Pid]),
Me.

sender(Pid,Request)->
Pid ! Request.


loop(Pid)->
receive
{From,transmit}->
% io:format("Process ~p transmitting~n",[self()]),
sender(Pid,{From,transmit}),
loop(Pid);
{update,Pid2} ->
% io:format("Process ~p has new target ~p~n",[self(),Pid2]),
loop(Pid2);
{report} ->
io:format("Process ~p has target ~p~n",[self(),Pid]),
sender(Pid,{report}),
loop(Pid);
{die} ->
exit(Pid,kill)
end.

tail_loop(I,Messages,Pid)->
receive
{From,transmit}->
case I < Messages of
true ->
% io:format("Tail Process ~p transmitting ~n",[self()]),
sender(Pid,{From,transmit}),
tail_loop(I+1,Messages,Pid);
false->
From!{done},
tail_loop(I,Messages,Pid)
end;
{update,Pid2} ->
% io:format("Tail Process ~p has new target ~p~n",[self(),Pid2]),
tail_loop(I,Messages,Pid2);
{report} ->
io:format("Tail process ~p has target ~p~n",[self(),Pid]),
tail_loop(I,Messages,Pid);
{die} ->
exit(Pid,kill)
end.