go言語でリングノードベンチマーク
go言語でリングノードベンチマークを書いてみました。
リングノードベンチマークとは、もともとプログラミングErlangという本に出てくる練習問題です。
- 作者: Joe Armstrong,榊原一矢
- 出版社/メーカー: オーム社
- 発売日: 2008/02/23
- メディア: 単行本(ソフトカバー)
- 購入: 8人 クリック: 284回
- この商品を含むブログ (97件) を見る
2. リングのベンチマークを書いてみよう。N個のプロセスからなるリングを作り、1つのメッセージがリングをM回まわるようにして、合計でN*M個のメッセージが送信されるようにする。さまざまなNとMの値について所要時間を測ってみよう。
自分が得意なほかのプログラミング言語で同様のプログラムを書いて結果を比べてみよう。ブログを書いて結果をインターネットに公開しよう!
ということなので、ブログを書いて公開してみることにしました。
go:
package main import ( "log"; "flag"; "fmt"; "time"; "strconv"; ) const (message = iota; stop) type Msg struct { command int; str string; } func main() { // コマンドライン引数の処理 flag.Parse(); if flag.NArg() != 3 { log.Exit("invalid argument.") } n , err := strconv.Atoi(flag.Arg(0)); if err != nil { log.Exit("setconv.Atoi:", err) } m , err := strconv.Atoi(flag.Arg(1)); if err != nil { log.Exit("setconv.Atoi:", err) } str := flag.Arg(2); ts := time.Nanoseconds(); // 時間計測開始 start(n, m, str); te := time.Nanoseconds(); // 時間計測終了 fmt.Printf("%d %d %f", n, m, float64(te - ts) / 1000000000); // N M 秒 } // メインの処理 func start(n int, m int, str string) { nch := make(chan Msg, 1); pch := make(chan Msg, 1); // リング作り makeRing(n, nch, pch); // リングにメッセージを投げる nch <- Msg{command:message, str:str}; for i := 0; i < m; i++ { msg := <- pch; nch <- msg; } nch <- Msg{command:stop}; // リングを壊す <- pch; // リングが壊れるのを待つ } func makeRing(n int, nch chan Msg, pch chan Msg) { var prevCh chan Msg = nch; for i := 0; i < n-1; i++ { nextCh := make(chan Msg, 1); go makeNode(nextCh, prevCh); prevCh = nextCh; } go makeNode(pch, prevCh); } func makeNode(nch chan Msg, pch chan Msg) { for { msg := <- pch; nch <- msg; if msg.command == stop { return } } }
比較用にerlang版も・・・
-module(ring). -export([start/3, makeNode/1]). start(N, M, Str) -> NextP = makeRing(N, self()), loop(M, {message, Str}, NextP), ok. loop(0, _, NextP) -> NextP ! stop, receive stop -> ok end; loop(M, Msg, NextP) -> NextP ! Msg, receive {message, _}=RecvMsg -> loop(M-1, RecvMsg, NextP) end. makeRing(0, TopP) -> TopP; makeRing(N, TopP) -> spawn(?MODULE, makeNode, [makeRing(N-1, TopP)]). makeNode(NextP) -> receive {message, _}=RecvMsg -> NextP ! RecvMsg, makeNode(NextP); stop -> NextP ! stop, ok end.
実行して比較してみました。
実行環境は
CPU: Core2Duo E8400(3GHz)
メモリ: 4GB
VMWare Player上のUbuntu 9.10(64bit)です。
1万個から10万個のプロセスを生成し、"hello"という文字列を100回まわすという試行を行い、かかった時間を計測しました。
erlangでの実行時間計測はerlシェル上で、たとえばN=10000,M=100,メッセージ="hello"の場合は、
timer:tc(ring, start, [10000, 100, "hello"]).
とすることで測ることができますが、erlangシェル上で実行した場合は前回に実行した時のキャッシュが残っていて、プロセス生成が高速になってしまう可能性があるので、公平になるようにシェルは毎回落とすことにします。
具体的には、
コマンドライン上で
erl +P 200000 -noshell -eval 'io:format("~w", [timer:tc(ring, start, [10000, 100, "hello"])]).' -s init stop
プロセス数N | メッセージ数M | erlang(秒) | go(秒) |
---|---|---|---|
10000 | 100 | 0.537184 | 0.756960 |
20000 | 100 | 1.053531 | 1.518783 |
30000 | 100 | 1.613506 | 2.255606 |
40000 | 100 | 2.113366 | 3.007530 |
50000 | 100 | 2.714717 | 3.729123 |
60000 | 100 | 3.259206 | 4.504331 |
70000 | 100 | 3.747574 | 5.254344 |
80000 | 100 | 4.338779 | 6.003126 |
90000 | 100 | 4.829708 | 7.083736 |
100000 | 100 | 5.436611 | 7.966264 |
私のコードではerlangのほうが速いようです。
もし最適化のアイデアなどがありましたら教えてください。
今回はgnuplotを使ってグラフ作りしたのですけれど、ラベルに日本語が使えないことがわかりました。
日本語対応版のgnuplotというのもあるらしいのですが、aptitude search gnuplotとしてもubuntuのパッケージにそれらしいものが見つからなかったので、それ以上の努力はやめて、ほかに良いグラフツールがないかと探していたらR言語という統計用の言語のグラフ機能が充実しているとの情報を見つけたので、ちょっと使ってみようかな?と思いました。
もともとR言語にも興味があったのですけれど。
お気軽にコメントしてください。