Scala と Erlang でリングベンチマーク [Scala]
[1] http://haskell.g.hatena.ne.jp/jmk/20070509/1178703858
[2] http://d.hatena.ne.jp/sumim/20070513/p1
リングベンチマークというのを Erlang と他の言語で作って比べてみると Erlang の軽量プロセスのすごさがわかるよという話。Scala で書いてみた。Scala のアクターを使ってみるのは初めて。
import scala.actors._
import scala.actors.Actor._
import Console._
import scala.actors.Exit
object Ring {
def main(args: Array[String]) = {
val begin = System.nanoTime()
start(args(0).toInt, args(1).toInt, args(2))
val end = System.nanoTime()
println(end - begin)
}
def makeActor(last:Boolean):Actor = {
actor {
react {
case neighbour:Actor =>
//println("I'm [" + self + "], my neighbour is [" + neighbour + "].");
loop {
react {
case (msg:Any, count:int) =>
//println("I'm [" + self + "], received a message: " + msg.toString() + "/" + count);
neighbour ! (msg, count - (if (last) 1 else 0));
//if (count == 1) exit()
if (count == 1) exit(if (last) 'notify_end else 'normal)
}
}
}
}
}
def start(n:int, m:int, msg:String) = {
val actors @ (h::t) = for (val i <- List.range(0,n)) yield makeActor(i == n - 1);
val neighbours = t ::: List(h)
for (val (actor, neighbour) <- actors.zip(neighbours)) actor ! neighbour;
self.trapExit = true
link(actors.last)
h ! (msg, m);
//receive { case ('EXIT, _, _) => () }
receive { case Exit(_, 'notify_end) => () }
}
}
2.5.0-RC2 で動くコードですが 2.4.0-final で動かすには最初の行の import scala.actors.Exit を消して println 以外のコメントアウト部分をその下の行と取り替えます。
1. 指定数分のアクターのリストを作る、このときアクター達は自分がメッセージを転送する先についてまだ知らないが、メッセージでそれを知らされるようにしておく。
2. アクターのリストを1つずらしたリストを作って元のアクターと zip する。ここで組になった相手が転送先のアクター。これでリング状になる。
3. メインのプロセスは最後のアクターの終了をトラップするようにしておく。
4. 最初のアクターにメッセージを送信。ここで「残り回数」をメッセージに付与する。残り回数を減らすのは最後のアクターの役目。
5. メインのプロセスは最後のアクターが終了するまで待機する。
という実装。これであってるのかなあ。これをこのまま Erlang にしたのが以下。
-module(ring).
-export([start/3, ready/1, loop/2]).
loop(Neighbour, Last) ->
receive
{Msg, Count} ->
%io:fwrite("I'm ~p, received a message: ~s/~w~n", [self(), Msg, Count]),
Neighbour ! {Msg, Count - (if Last -> 1; true -> 0 end)},
if
Count /= 1 -> loop(Neighbour, Last);
true -> true
end
end.
ready(Last) ->
receive
Neighbour ->
%io:fwrite("I'm ~p, my neighbour is ~p.~n", [self(), Neighbour]),
loop(Neighbour, Last)
end.
make_node(Last) ->
if
Last -> spawn_link(ring, ready, [Last]);
true -> spawn(ring, ready, [Last])
end.
start(N, M, Msg) ->
Nodes = [H|T] =
lists:map(fun(I) -> make_node(I == N) end, lists:seq(1, N)),
Neighbours = T ++ [H],
lists:foreach(
fun({Node, Neighbour}) ->
Node ! Neighbour end, lists:zip(Nodes, Neighbours)
),
process_flag(trap_exit, true),
lists:last(Nodes) ! {Msg, M},
receive
{'EXIT', _Caller, _Reason} -> ok
end.
Scala は Windows 上で動かしていて Erlang は玄箱にしか入れていないのでまともな比較できないのだけど、Windows (Celeron 2.80GHz) 上の Scala で N=1000,M=1000 が約11~16秒、玄箱 (PowerPC 200MHz) の Erlang で同じ入力の結果が約10秒なので環境をあわせなくても Erlang 圧勝は間違いないみたい。
Scala のアクターは精通していないので「○○じゃなくて××を使えばもっと速い」とかがある可能性も考えられる。
コメント 0