こんにちは。技術開発部で部長をしている伊良子と申します。
前回は、Luaを採用した経緯/理由について書くことで、Lua及びngx_luaの紹介をさせて頂きました。連載第二回の今回はngx_luaをより高速に動作させるためのTIPSをご紹介したいと思います。
LuaJITを利用する
Luaの最新バージョンは5.3ですが、ngx_luaがサポートしているのは5.1までです。ngx_luaは、このLua5.1を使用するよりもJITコンパイラ版であるLuaJITの使用を推奨しています。LuaJITはLua5.1系に対応した本家Luaとは別のプロジェクトで、本家Luaと比べて高速に動作します。
弊社でもngx_luaを利用し始めた当初は本家Luaを使用していたのですが、LuaJITに切り替えた際には1.3倍以上のパフォーマンス向上となりました。実際のアプリケーションでの速度比較なため、速度低下の要因は様々あるにも関わらずLuaJITに変えただけで1.3倍高速になったのには驚きました。
ngx_luaでの利用ではありませんが、本家LuaとLuaJITでベンチマーク比較してみましょう。
1 2 3 4 5 6 |
function fibonacci(n) if n <= 2 then return 1 end return fibonacci(n - 2) + fibonacci(n - 1) end print(fibonacci(40)) |
フィボナッチ数列の40番目の数を求めるプログラムで比較してみます。このような処理だと上記したような実際のWebアプリケーションと違い、処理系のみの比較となります。
1 2 3 4 5 6 7 8 9 10 11 12 |
$ time /usr/local/bin/lua fibonacci.lua 102334155 real 0m12.120s user 0m12.117s sys 0m0.000s $ time /usr/local/luajit/bin/luajit fibonacci.lua 102334155 real 0m1.121s user 0m1.120s sys 0m0.000s |
LuaJITが10倍の速度差で圧勝しました。
ngx.shared.DICTの利用
ngx.shared.DICTは、子プロセス間で共通に使える記憶領域です。
1 2 |
ngx.shared.persons:set("age", 19) ngx.shared.persons:get("age") |
のような形で使えます。memcachedと似たような利用方法になりますが、特筆すべきはその速度です。memcachedとngx.shared.DICTを、ApacheBencheを使った比較です。
memcachedでSET/GETを30回づつ行った場合:400〜500req/sec
同様のSET/GETをngx.shared.DICTで行った場合: 1200〜1400/sec
何もしないでレスポンスだけ返す場合: 1300〜1400req/sec
ngx.shared.DICTを使った場合は、何もしない場合と変わらない速度で動作しました。直接メモリーを読み書きするため非常に高速です。弊社では、memcachedのキャッシュとして利用しています(キャッシュのキャッシュ)
ngx.varやngx.req.XXXを何度も呼ばない
ngx.varやngx.reqは、参照するたびに、ngx.var.全XXX、ngx.req.全XXXを展開する仕様であるらしく、参照のコストが高いです。
1 2 3 |
local headers = ngx.req.get_headers() local params = ngx.req.get_uri_args() local ngx_var = ngx.var |
のように一度だけ参照して、以後はその変数(テーブル)を使いまわすようにします。
os.timeではなくngx.timeを使う
luaの標準ライブラリであるos.timeを使うと、使う度にシステムコールが発生してしまいますが、ngx.timeを使えばnginxがキャッシュした時間を返すため高速です。
今回はこの辺で。
ネタもなくなってきたので次回でLuaは最終回とさせて頂きます。