« Erlang楽しい♪ | Main | oregexp動かない・・・ »

Wednesday, April 18, 2007

ErlangとPerlの速度比較

Erlangは並列処理が得意なので,それに関しては他の言語より圧倒的に速いのは確認したけれど, わたしがよく使う通常の処理に関してもどの程度違うのか測定してみた.
一般的な処理に関しては,The Computer Language Shootout Benchmarks にたくさん結果があるので,そちらが参考になる.

言語としては,どちらも動的型付けで,コンパイラで内部表現に直してからインタープリタで実行,というあたりは一緒.
(ただしErlangはnativeコンパイラが別途ある)
ほぼ同じくらいの速度が出ても良さそうなところ.

Perlの代替を考えていたので,比較対象はPerl.
Language Shootout によれば,Erlang の方が多くのテストで2~3倍前後高速.nativeコンパイルすると,その差がさらに広がる感じ.

Erlang 5.5.4 と Perl v5.8.5.
SuSE Linux 9.2 (x86_32) でメモリ4G,Xeon 3G×2の環境で測定.
測定は5回ずつ行って,中央値を採用.

・" " を 0xfffff個 join

文字列の扱いの比較.Erlangは,StringとBinaryの2種類があるので,両方で測定した.
Stringは1文字あたり32bit環境で8バイト,64bit環境で16バイト使うので,あまり大きなデータには使えない.
Binaryは32bit環境で文字数+12~24バイト,64bit環境で文字数+24~48バイト.同じバイナリは共有される.
Binaryは扱いにくい部分があるので,使い分けが必要そう.

方法言語時間(sec)
文字列はStringErlang (beam)0.312705
Erlang (native)0.118335
Erlang (beam+smp)0.235028
Erlang (native+smp)0.142893
文字列はBinaryErlang (beam)0.458387
Erlang (native)0.388640
Erlang (beam+smp)0.415061
Erlang (native+smp)0.343201
配列にpushしてjoinPerl0.741943
.=でつなげるPerl0.538587

Perlは配列にしてつなげるより,.=でつなげた方が速かった.文字列が短いから配列管理のオーバーヘッドの方が大きいのかも? いずれの方法でもErlangの方が全般的に高速.

・"123あいう" の文字コードを 0xffff回変換

文字コードをUTF-8からShift_JISに変換する速度を測定.
Erlangはiconvを利用するモジュールが(非標準で)あり,それしか方法がないのでそれで測定. nativeコンパイルに関しては,iconvを利用するモジュール自体はnativeコンパイルなしで, ベンチを実行する側のみとした.
Perlは,Unicode::JapaneseとEncodeの両方で測定した.
どちらもモジュールのロードなどの時間は除いて,純粋に変換部分だけの時間を測定.

方法言語時間(sec)
iconvErlang (beam)0.604394
Erlang (native)0.612490
Erlang (beam+smp)1.232886
Erlang (native+smp)1.250170
Unicode::JapanesePerl0.646837
EncodePerl1.403991

Erlangはsmpにすると極端に処理が遅くなってしまう.
Cのモジュールと通信してデータをやりとりする仕様だけれど, C側のモジュールがシングルスレッドで実行されて, そのモジュールとの通信を奪い合うのが原因?なのかなぁ.
C側のモジュールとのやりとりをマルチスレッドで実行させる方法があるのかもしれないけど, まだそこまで調べられてない状態. マニュアルをざっと眺めた限りでは無理そう...
どうも,外部プログラムは,Erlang内の1プロセスと同様の扱いになっていて, 漢字コードの変換のような,内部的な処理の高速化にCコードを使うのには向いていない感じ.
Erlangコードで漢字コード変換を書いて,それをnativeコンパイルした方が速いかもしれない.

Perl側は,Encodeが遅い.
Encode::from_toがinplaceでコンバートしてしまうために,直前に文字列コピーがあるのも影響しているのかも?
もう少し速くても良さそうだけども...
コードは以下のような感じ.
(UniJP側は,newする時間は含まない形で測定)
my $uj = new Unicode::Japanese;
my $t0 = [gettimeofday];
for(my $i = 0; $i < 0xffff; $i++) {
        $uj->set("123あいう")->sjis;
}
my $str = "123あいう";
for(my $i = 0; $i < 0xffff; $i++) {
        my $str2 = $str;
        Encode::from_to($str2, "UTF-8", "Shift_JIS");
}


・正規表現でアクセスログからURL部分を0xfff回取得

正規表現で,アクセスログからURLの部分を取得するテスト.メソッドとHTTP/1.1の部分も含めて取得する.
Erlangは標準ライブラリにregexpがあるので,それを利用.

方法言語時間(sec)
regexpErlang (beam)2.841569
Erlang (native)3.163096
Erlang (beam+smp)2.474175
Erlang (native+smp)2.693628
正規表現Perl0.012415


Perlの正規表現の圧勝.
というより,Erlangのregexpモジュールはかなり機能が貧弱なようで,普通には実装できなかった.
無理に正規表現で済ませようとした結果なので,速度が遅いと言うよりは機能不足かな.
その上,ソースを見てみたら,普通にErlangのコードで実装されていた. これでは言語自体に内蔵されているPerlにかなうはずが無い…
Perlなら,正規表現の中で()で一部分を指定して,それを$1で後から取得できる. でも,Erlangのregexpにはそのような機能がない.
結局以下のようなコードになってしまった.
bench() ->
        {ok, Reg1} = regexp:parse(
            "^[^ ]+ [^ ]+ [^ ]+ \\[[^\\]]+\\] \""),
        {ok, Reg2} = regexp:parse(
            "^[^ ]+ [^ ]+ [^ ]+ \\[[^\\]]+\\] \"([^\"]+)\""),
        String = "12.34.123.12 - - [17/Apr/2007:23:53:13
         +0900] \"GET /html/123.html HTTP/1.1\" 200 6980
         \"http://ayaya.jp/html/123.html\" \"Mozilla/4.0
          (compatible; MSIE 6.0; Windows NT 5.1; SV1)\"\n",
        Time = timer:tc(strregexp2, bench,
            [16#fff, Reg1, Reg2, String]),
        io:format("~w~n", [Time]).

bench(0, _Reg1, _Reg2, _String) ->
        0;
bench(N, Reg1, Reg2, String) ->
        {match, _Start1, Len1} = regexp:match(String, Reg1),
        {match, _Start2, Len2} = regexp:match(String, Reg2),
        Match = string:substr(String, Len1 + 1, Len2 - Len1 - 1),
        bench(N - 1, Reg1, Reg2, String).


この辺は,regexpじゃなくて,C言語的に実装しなければならないのかなぁ.
正規表現は便利なだけに,これはいまいち.


----
追記.
erl_driverのマニュアルを読むと,ロックレベルを変えることで,複数スレッドで同時に使えるようになる模様.
後日試してみよう・・・

|

« Erlang楽しい♪ | Main | oregexp動かない・・・ »

Comments

The comments to this entry are closed.

TrackBack


Listed below are links to weblogs that reference ErlangとPerlの速度比較:

» tips - Encodeを速く使う方法 [404 Blog Not Found]
はっきり言ってこれはフェアではない。 みかログ: ErlangとPerlの速度比較Perl側は,Encodeが遅い. Encode::from_toがinplaceでコンバートしてしまうために,直前に文字列コピーがあるのも影響しているのかも なぜなら、Encode::from_to()は速度ではなく、安全性に...... [Read More]

Tracked on Monday, April 23, 2007 01:40 AM

« Erlang楽しい♪ | Main | oregexp動かない・・・ »