ISUCON2015で仕込んでいたネタ(h2o + mruby)の供養をする記事
ISUCON2015予選お疲れさまでした。
予選関連エントリとしてはかなり出遅れてしまいましたが、自分がISUCONに向けて仕込んでいたものの、日の目を見なかったネタの供養をしたいと思います。
燃えろよ燃えろ。
(自宅でBeagleBoneBlackつかって動かしてたブログが消滅してたのでこっちで書くジャバよ。)
参加チーム
アルパカ三姉妹
です。パカー。
ISUCONへの参加が初めてなのは自分だけだったので、足を引っぱるまいと気負いが凄かったと反省。
仕込んでいたもの
以前からmatsumoto-rさんのブログエントリ(その1, その2)によって、試したい気分が高まっていたh2oのmruby拡張をネタに一発逆転を目論み、sinatraアプリの一部ルーティングをmrubyで書けるような何かを作ろうとしました。
結果、
の2つのmrbgemsと、これら+sinatraアプリを移植するために必要そうなmrbgemsを組みこんだh2oのフォーク
を作りました。
mruby-r3
Cで書かれた高速なルーティングライブラリR3のバインディングです。
今回に必要そうなものしか書いていません。
mruby-rack-r3
最近、h2oのmruby拡張のインターフェースがrack準拠になっていたため、includeすることでsinatraライクなDSLを使えるようにするモジュールです。
mrubyオンリーのmrbgemsです。
それぞれREADMEをご覧ください。
なぜ使わなかったか
アプリケーションコードの改善で沼に嵌ってしまい、余裕がなかった。
(それでも使ってみればよかったのでは???????)
ベンチマーク
これだとコード達が浮かばれないのでベンチマークを取って供養します。
ベンチマークに使った環境はGCEのn1-highcpu-16、ubutu-15.04です。
https://github.com/rail44/h2o/tree/mrub-rack-r3github.com
↑のブランチ上で
./install_deps_ubuntu.sh cmake -DWITH_MRUBY=ON . make h2o
とすることで、mruby-rack-r3とおまけにmruby-redisが同梱されたh2oバイナリが作成できます。
./h2o -c examples/h2o-mruby/h2o.config
とすることでベンチマーク用のサンプルアプリケーションが立ち上がります。 アプリケーションコードはこんな感じ。
class App include Rack::R3 def redis @redis ||= Redis.new('127.0.0.1', 6379) end get '/hello/{first_name}/{last_name}' do |f, l| [200, {'content-type' => 'text/plain; charset=utf-8'}, ["Hello #{f} #{l}!\n"] ] end get '/incr' do [200, {'content-type' => 'text/plain; charset=utf-8'}, ["#{redis.incr('hoge')}\n"] ] end end App.new
また、これと同等なsinatraアプリケーションを rubyapp_for_bench
に書きました。
h2oの並列ワーカー数4に対し、pumaの並列ワーカー数16、最大スレッド16と大きく取ることで、pumaのデーモンが足らなくなることを防いだ上で性能を比較してみます。
パラメータとして受けとった文字列を結合するエンドポイント
satoshi@train-isucon5-rail44 ~/h2o> curl -D - http://localhost:8000/hello/Satoshi/Amemiya HTTP/1.1 200 OK Date: Thu, 01 Oct 2015 08:19:57 GMT Server: h2o/1.5.0 Connection: keep-alive Content-Length: 23 content-type: text/html;charset=utf-8 x-xss-protection: 1; mode=block x-content-type-options: nosniff x-frame-options: SAMEORIGIN Hello Satoshi Amemiya!
sinatra版
Running 30s test @ http://localhost:8000/hello/Satoshi/Amemiya 4 threads and 100 connections Thread Stats Avg Stdev Max +/- Stdev Latency 4.79ms 3.36ms 58.19ms 75.05% Req/Sec 5.54k 397.66 8.34k 83.42% 662753 requests in 30.09s, 174.45MB read Requests/sec: 22027.56 Transfer/sec: 5.80MB
mruby-rack-r3版
Running 30s test @ http://localhost:8000/hello/Satoshi/Amemiya 4 threads and 100 connections Thread Stats Avg Stdev Max +/- Stdev Latency 1.23ms 5.15ms 202.99ms 99.81% Req/Sec 24.25k 1.89k 38.18k 64.78% 2905104 requests in 30.10s, 559.65MB read Requests/sec: 96514.58 Transfer/sec: 18.59MB
はやいo(^^)o
Redisへのアクセスが発生するエンドポイント
satoshi@train-isucon5-rail44 ~/h2o> curl -D - http://localhost:8000/incr HTTP/1.1 200 OK Date: Thu, 01 Oct 2015 08:23:21 GMT Server: h2o/1.5.0 Connection: keep-alive Content-Length: 1 content-type: text/html;charset=utf-8 x-xss-protection: 1; mode=block x-content-type-options: nosniff x-frame-options: SAMEORIGIN 1⏎ satoshi@train-isucon5-rail44 ~/h2o> curl -D - http://localhost:8000/incr HTTP/1.1 200 OK Date: Thu, 01 Oct 2015 08:23:23 GMT Server: h2o/1.5.0 Connection: keep-alive Content-Length: 1 content-type: text/html;charset=utf-8 x-xss-protection: 1; mode=block x-content-type-options: nosniff x-frame-options: SAMEORIGIN 2⏎
sinatra版
Running 30s test @ http://localhost:8000/incr 4 threads and 100 connections Thread Stats Avg Stdev Max +/- Stdev Latency 5.12ms 3.56ms 69.91ms 74.28% Req/Sec 5.20k 425.08 7.68k 75.83% 621764 requests in 30.06s, 152.88MB read Requests/sec: 20684.90 Transfer/sec: 5.09MB
mruby-rack-r3版
Running 30s test @ http://localhost:8000/incr 4 threads and 100 connections Thread Stats Avg Stdev Max +/- Stdev Latency 2.32ms 2.68ms 199.00ms 99.95% Req/Sec 11.06k 1.39k 14.51k 58.72% 1325187 requests in 30.10s, 234.01MB read Requests/sec: 44026.30 Transfer/sec: 7.77MB
早いものの、先ほどのエンドポイントに比べ、速度の向上具合が緩やかになっています。
ここからは、今後時間ができたときにじっくり考えたい内容ですが、mrubyのコード中でredisへのアクセスが発生した際にh2oがそのスレッドを開放できず、イベントループの恩恵が得られてないのではないかと仮説を立てています。
(どうやって確かめればいいのだろう)
まとめ
ISUCONく"や"し"い"