mod_mrubyにはぜひluajit版に勝って、言語の性能の差がシステム全体の決定的な差でないことを教えてやる と言ってほしい。すっかり他力本願モード
— Miura Hidekiさん (@miura1729) 1月 23, 2013
というツイートに触発されて、mod_luaのLuaJIT版の速度がどの程度早く、mod_mrubyと比較してどれほどの性能差があるのかを試してみました。
mod_luaのLuaJIT版の設定
ここが結構はまってしまって、最新のApache2.4.3ソースで–enable-luajitとしても、LuaJIT版でmod_luaが動作するようにはなっていません。バグかな?
というわけで、Apacheのソースを追って無理やりmod_luaをLuaJIT版に対応させました。まずは、普通に以下のようにconfigureを実行します。
[program lang=’bash’ escaped=’true’]
./configure --prefix=/usr/local/apache243 --with-apr=/usr/local/apr --with-apr-util=/usr/local/apr --enable-modules=all --enable-mods-shared=all --enable-mpms-shared='prefork worker event' --enable-lua --enable-luajit
[/program]
これだけではLuaJIT版できちんと動作しないので、以下のように修正します。
build/config_vars.mkを弄る
以下のように変更します。
[program lang=’bash’ escaped=’true’]
(snip) MOD_LUA_LDADD = -lluajit-5.1 -lm -ldl (snip) EXTRA_CPPFLAGS = -DLINUX=2 -D_REENTRANT -D_GNU_SOURCE -D_LARGEFILE64_SOURCE (snip)
[/program]
やったこととしては、LuaJITのライブラリの指定とlibdlを指定すること、EXTRA_CPFLAGSに書かれていた-DAP_ENABLE_LUAJITを削除することです。これでmakeすれば、LuaJIT版mod_luaで動作します。
mod_luaのLuaJIT版がいかに早いか
LuaJIT版のmod_luaの速度を試してみましょう。
mod_luaの通常版と、mod_luaのLuaJIT版、そして、mod_mrubyそれぞれにフィボナッチ数列の計算をさせてみました。コードは以下です。
luaのコード
[program lang=’lua’ escaped=’true’]
require "apache2" function fib(n) if n < 2 then return n end return fib(n-2) + fib(n-1) end function handle(r) r:puts(fib(37)) return 200 end
[/program]
mrubyのコード
[program lang=’ruby’ escaped=’true’]
def fib(n) return n if (n < 2) return fib(n - 2) + fib(n - 1) end Apache.rputs fib(37).to_s
[/program]
これらに対して、curlとtimeコマンドで処理速度を比較すると以下のようになりました。ちなみに、Apacheモジュール版ではなく、lua、luajit、mruby、rubyコマンドで上記のコード(r:putsやApache.rputsを標準のputsにしたもの)を実行した時の処理時間も参考のため載せておきます。
mod_lua
(通常) |
mod_lua
(LuaJIT) |
mod_mruby
(最新) |
lua
(5.1.4 ) |
luajit
(1.1.8) |
mruby
(最新) |
ruby
(1.9.3p327) |
|
処理時間(real time)(秒) | 11.346 | 0.514 | 10.144 | 11.614 | 2.190 | 10.664 | 7.494 |
このようになりました。
まずは、LuaJITが早すぎですね。これはびっくりしました。しかし、mod_lua(LuaJIT版)がluajitコマンドより4倍程早いのはなぜでしょう。また、mrubyがLuaよりも少しだけ早い事がわかりました。しかし、Ruby1.9.3の方が早いですね。
言語の性能の差がApacheモジュールの決定的な差でないことを教えてやる
というわけで、ここからが本題です。
現状、LuaJIT版のmod_luaには明らかに言語の処理速度では勝てなさそうに思えます。しかし、「この言語の性能の差がApacheモジュールの決定的な差でないことを教えてやる」というわけでいつものhello worldを出力するベンチマーク勝負を行なってみました。なぜいつもスクリプトの処理をできるだけ軽量にするかというと、言語による処理速度を最低限にすることで、Apacheモジュールとしてのアーキテクチャの差を最大限に出して比較をしたかったためです。
実際に、hello worldを出力するluaとmrubyのそれぞれのスクリプトに対して、同時接続数100総接続数10万でベンチを行うと、以下のようになりました。Apacheのバージョンは2.4.3のevent MPMで行いました。
mod_lua(通常) | mod_lua(LuaJIT版) | mod_mruby | |
リクエスト処理数/sec | 5758.86 | 6429.34 | 13442.97 |
ということで、Apacheモジュールのアーキテクチャの性能比較を行った場合は、このようにmod_mrubyの方がかなり早い事がわかります。まさにこれは。
言語の性能の差がApacheモジュールの決定的な差でないことを教えてやる!
ということですね。(少しだけ安心しました。これでLuaJIT版に惨敗していたら一から勉強し直しだなぁ、なんてどきどきしながらベンチをとっていました。)
どうしてこのような差がでるのかというと、これまでブログを読んでいる方は大体分かっていると思いますが、mod_luaがリクエスト毎にステートマシンを生成して破棄しているのに対し、mod_mrubyはサーバ起動時にステートマシンを生成して、複数のリクエストでそのステートマシンを使いまわしているためです。
まとめ
以上のように、 フィボナッチ数列の計算速度では負けてしまいましたが、Apacheモジュールのアーキテクチャの性能比較としてはmod_mrubyに軍配が上がるのではないかと思われます。
しかし、フィボナッチ数列の計算のような処理等、処理に時間のかかるような実装を記述する場合は、mod_luaのLuaJIT版がその性能を発揮するでしょう。一方で、そこまで処理に時間のかからないような実装を記述する場合は、Apacheモジュールとしてのアーキテクチャの優位性によって、mod_mrubyの方が性能を発揮するように思われます。
これらをまとめると、
- コードの処理がボトルネックにならない程度の実装の場合はmod_mrubyを使う方が効率が良い
ということになるでしょう。
ちなみに、上記のツイートだけでなく以下のようなツイートにも勇気付けられました。
ぜひ matsumotory さんには ApacheCon で「じっとしてろ!(JITだけに)」って言って欲しい。
— mattnさん (@mattn_jp) 1月 23, 2013