ngx_mruby vs lua-nginx-module 対決してみた

論文や発表が少し落ち着いたので、研究の実装を再開しています。

そこでふと思ったのですが、そういえばngx_mrubylua-nginx-moduleのパフォーマンス比較していないなぁと思いました。でも、今検証環境が色々あって揃っていないので微妙だなぁと思いつつも、やっぱり気になると試さずにはいられないたちなので、簡単なhello worldのベンチマーク比較をしてみました。

ngx_mrubyとlua-nginx-moduleの導入

検証環境がないので一から導入しました。意外と簡単ですね。

[program lang=’bash’ escaped=’true’]

wget http://nginx.org/download/nginx-1.4.3.tar.gz
tar xvf nginx-1.4.3.tar.gz 
git clone git@github.com:matsumoto-r/ngx_mruby.git
cd ngx_mruby
git submodule init
git submodule update
./configure --with-ngx-src-root=../nginx-1.4.3 --with-ngx-config-opt="--prefix=/usr/local/nginx143"
make build_mruby
cd ..
git clone https://github.com/chaoslawful/lua-nginx-module.git
git clone https://github.com/simpl/ngx_devel_kit.git
cd nginx-1.4.3
./configure --prefix=/usr/local/nginx143 --add-module=../ngx_devel_kit --add-module=../ngx_mruby --add-module=../lua-nginx-module

[/program]

とまぁ、大体こんな感じですぐにインストールできました。

続いて、以下のような設定をnginx.confに書きました。

[program lang=’text’ escaped=’true’]

location /lua {
    content_by_lua 'ngx.say("hello lworld")';
}

location /mruby {
    mruby_content_handler_code 'Nginx.rputs "hello mworld¥n"';
}

[/program]

/luaにアクセスしたらlua-nginx-moduleでhello worldして、/mrubyにアクセスするとngx_mrubyでhello worldする最もシンプルな処理です。では、これらに対していつものようにベンチマークしてみましょう。

ベンチマーク

今回は、いつもの検証環境がないので、localhost向けでabコマンドを実行しました。ホストはCPUコア1つ、メモリ1GB、NIC1Gbpsの仮想環境にFedora19をいれました。

また、HTMLの出力は同一の13バイトに合わしていますが、それぞれのモジュールでデフォルトのHTTPレスポンスヘッダが違うので、トータルの転送量は微妙に変わります。それらを考慮した上で、以下のベンチマーク結果を見ると良いと思います。keepaliveの有無と同時接続数100総接続数10万でrequest/secを計測しました。

lua-nginx-module

keepalive無し

[program lang=’text’ ]

$ /usr/local/apache/bin/ab -c 100 -n 100000 http://127.0.0.1/lua

Server Software:        nginx/1.4.3
Server Hostname:        127.0.0.1
Server Port:            80

Document Path:          /lua
Document Length:        13 bytes

Concurrency Level:      100
Time taken for tests:   7.197 seconds
Complete requests:      100000
Failed requests:        0
Write errors:           0
Total transferred:      16900000 bytes
HTML transferred:       1300000 bytes
Requests per second:    13894.30 [#/sec] (mean)
Time per request:       7.197 [ms] (mean)
Time per request:       0.072 [ms] (mean, across all concurrent requests)
Transfer rate:          2293.10 [Kbytes/sec] received

[/program]

keepalive有り

[program lang=’text’ escaped=’true’]

$ /usr/local/apache/bin/ab -k -c 100 -n 100000 http://127.0.0.1/lua

Server Software:        nginx/1.4.3
Server Hostname:        127.0.0.1
Server Port:            80

Document Path:          /lua
Document Length:        13 bytes

Concurrency Level:      100
Time taken for tests:   2.691 seconds
Complete requests:      100000
Failed requests:        0
Write errors:           0
Keep-Alive requests:    99006
Total transferred:      17395030 bytes
HTML transferred:       1300000 bytes
Requests per second:    37156.92 [#/sec] (mean)
Time per request:       2.691 [ms] (mean)
Time per request:       0.027 [ms] (mean, across all concurrent requests)
Transfer rate:          6311.97 [Kbytes/sec] received

[/program]

ngx_mruby

keepalive無し

[program lang=’text’ escaped=’true’]

$ /usr/local/apache/bin/ab -c 100 -n 100000 http://127.0.0.1/mruby

Server Software:        nginx/1.4.3
Server Hostname:        127.0.0.1
Server Port:            80

Document Path:          /mruby
Document Length:        13 bytes

Concurrency Level:      100
Time taken for tests:   5.148 seconds
Complete requests:      100000
Failed requests:        0
Write errors:           0
Total transferred:      12900000 bytes
HTML transferred:       1300000 bytes
Requests per second:    19423.42 [#/sec] (mean)
Time per request:       5.148 [ms] (mean)
Time per request:       0.051 [ms] (mean, across all concurrent requests)
Transfer rate:          2446.90 [Kbytes/sec] received

[/program]

keepalive有り

[program lang=’text’ escaped=’true’]

$ /usr/local/apache/bin/ab -k -c 100 -n 100000 http://127.0.0.1/mruby

Server Software:        nginx/1.4.3
Server Hostname:        127.0.0.1
Server Port:            80

Document Path:          /mruby
Document Length:        13 bytes

Concurrency Level:      100
Time taken for tests:   1.991 seconds
Complete requests:      100000
Failed requests:        0
Write errors:           0
Keep-Alive requests:    99001
Total transferred:      13395005 bytes
HTML transferred:       1300000 bytes
Requests per second:    50227.56 [#/sec] (mean)
Time per request:       1.991 [ms] (mean)
Time per request:       0.020 [ms] (mean, across all concurrent requests)
Transfer rate:          6570.30 [Kbytes/sec] received

[/program]

このような結果になりました。結果だけ見ると、この環境でのこのベンチマーク方法ではngx_mrubyの方が1.4倍くらい速い事になります。表にまとめると以下のようになります。

Request/sec
ngx_mruby lua-nginx-module
keepalive無し 19423.42 13894.30
keepalive有り 50227.56 37156.92

対象のコンテンツをhello world程度の軽量な処理にすることで、インタプリタに関連する処理の影響を最大限にすることが今回のベンチマークの目的です。ngx_mrubyは基本的にmod_mrubyで考えた高速かつ省メモリなアーキテクチャで実装しているので、機能面や使いやすさを前提としたときに、今思いつく限りでの高速な実装になっています。その辺りは論文でも書いているので、また公開します。

luaとmrubyの言語としての速度はluaの方が現段階では速いと思うので、今回lua-nginx-moduleが遅かったのはインタプリタ組み込みアーキテクチャにおける性能差のように推測します。その辺りについては、今後lua-nginx-moduleのコードを追っていきたいと思います。

まとめ

単純なhello worldの比較ではngx_mrubyの方が速い結果となりました。しかし、lua-nginx-moduleには沢山機能があって、安定稼働している例もいくつか聞くので、まだまだ複雑な事をやろうとするとlua-nginx-moduleの方が良いでしょう。また、コルーチンを使ったasync+non-blockingな動きを実装済み(lua-nginx-module の紹介 ならびに Nginx+Lua+Redisによる動的なリバースプロキシの実装案)ですので、その辺りでも優位性があります。しかし、そろそろngx_mrubyでできるような処理(ngx_mrubyの紹介 ならびに nginx+mruby+Redisによる動的なリバースプロキシの実装案)であれば、ngx_mrubyを使う選択肢も出てきたのではないでしょうか。

ngx_mrubyの今後の実装方針としては、このようなパフォーマンスを低下させることなく、mod_mrubyの機能をベースに機能追加していきたいと思います。少なくとも、ngx_mrubymod_mrubyはできるだけ同じようなDSL記述が可能になるように工夫していく予定です。