go言語でリングノードベンチマーク

go言語でリングノードベンチマークを書いてみました。
リングノードベンチマークとは、もともとプログラミングErlangという本に出てくる練習問題です。

プログラミングErlang

プログラミングErlang

以下引用:

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版も・・・

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言語にも興味があったのですけれど。

お気軽にコメントしてください。

windowsでgo言語環境構築

mingw + msys + gnuwin32 + go で開発環境を構築したい

やること
  • mingwをインストール
  • msysをインストール
  • GetGnuWin32でいろんなパッケージを一括インストール

コンパイル時に/bin/edを直接呼び出してくるみたいなので、ed.exeだけ/bin/にコピーしておく。

$HOME/.bashrcを編集

export GOROOT=$HOME/hectorchu-go-windows
export GOARCH=386
export GOOS=mingw
export GOBIN=$HOME/bin

export PATH=$HOME/bin:$PATH

必要なディレクトリを作成

$ madir ~/bin

ソースを取ってくる

$ cd
$ hg clone https://hectorchu-go-windows.googlecode.com/hg/ hectorchu-go-windows

そのままコンパイルを始めるとquietgccがないと文句を言われるのでこうする

$ cp ~/hectorchu-go-windows/src/quietgcc.bash ~/bin/quietgcc

covとprofのコンパイルに失敗するみたいなので、仮に修正しておく。(将来はちゃんとコンパイルできると期待して)
hectorchu-go-windows/src/cmd/make.bash 21行目

for i in cc ${O}l ${O}a ${O}c gc ${O}g gopack nm cov godefs prof gotest

for i in cc ${O}l ${O}a ${O}c gc ${O}g gopack nm godefs gotest

$ cd ~/hectorchu-go-windows/src/
$ ./all.bash

これで~/binに一応動くものができると思う。