レイトレースベンチの並列テスト

プログラミングErlangの20章にlists:mapを置き換えるだけで並列化できますよ、
と書いてあったのでやってみた。

置き換えたのはこのレイを飛ばす部分。

% オーバーサンプリング(N x N倍サンプリングして平均する)
sampling({PIXEL_X, PIXEL_Y}, Scene, N, GET_RAY)->
  average_color(
%    lists:map(fun({X, Y})->ray_trace(GET_RAY(X, Y), Scene) end, 
% ↓並列版
    lib_misc:pmap(fun({X, Y})->ray_trace(GET_RAY(X, Y), Scene) end, 
      over_sampling({PIXEL_X, PIXEL_Y}, N))
  ).

Core DUO(Windows XP)で計測してみました。

>erl -noshell -s renderer main tmp.bmp 200 3
{57046000,ok}
>erl -noshell -s renderer main tmp.bmp 200 3 -smp +S 1
{102031000,ok}
>erl -noshell -s renderer main tmp.bmp 200 3 -smp +S 2
{56812000,ok}
>erl -noshell -s renderer main tmp.bmp 200 3 -smp +S 3
{58890000,ok}

通常のmapを使った版

>erl -noshell -s renderer main tmp.bmp 200 3
{88109000,ok}

並列無しと2並列とでは1.8倍くらい高速になった。
mapを並列版に差し替えた分のオーバーヘッドがあるので、
2並列程度ではうれしさが少なめな感じです。
4コアとかだったらよさそう。

ただ、レイトレースの書き方に問題があって
シーンの情報をいちいち全部渡していることでメッセージを最小化できていないことが性能に影響しているかもしれない。
(小さいシーンなので影響はないかもしれない)

次は分散版のmapを作ってどんな効果があるのか試してみたいと思う。
今回使ったマシン以外にOSX, Linux, Windowsを一台ずつ参加させて4台5コアで実験予定。
ちなみに、分散版のmapはプログラミングErlangには載って
いなかったので関連する章を読んで勉強する必要がありそうですw

コマンドライン実行用の追加コード。

引数がアトムのリストで渡ってくるので文字列に変換している。
必要な場合はさらに数値に変換している。

% コマンドライン用
main([F, Size, L])->
  io:write(timer:tc(renderer, compatible, [
  	atom_to_list(F), 
	list_to_integer(atom_to_list(Size)), 
	list_to_integer(atom_to_list(L))
	])),
  init:stop().

プログラミングErlangより

並列版map。

-module(lib_misc).
-export([pmap/2]).

pmap(F, L)->
  S=self(),
  Ref=erlang:make_ref(),
  Pids=lists:map(fun(I)-> 
		spawn(fun()-> do_f(S, Ref, F, I) end)
	   end, L),
  gather(Pids, Ref).	

do_f(Parent, Ref, F, I)->
  Parent ! {self(), Ref, (catch F(I))}.

gather([Pid|T], Ref)->
  receive
    {Pid, Ref, Ret}->[Ret|gather(T, Ref)]
  end;
gather([], _) ->
  [].

Erlangは最初からこの本ではじめるべきと思う。
7章が好き。