SSブログ

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 のアクターは精通していないので「○○じゃなくて××を使えばもっと速い」とかがある可能性も考えられる。


nice!(0)  コメント(0)  トラックバック(0) 
共通テーマ:パソコン・インターネット

nice! 0

コメント 0

コメントを書く

お名前:
URL:
コメント:
画像認証:
下の画像に表示されている文字を入力してください。

トラックバック 0

この広告は前回の更新から一定期間経過したブログに表示されています。更新すると自動で解除されます。