読者です 読者をやめる 読者になる 読者になる

ISUCON2015で仕込んでいたネタ(h2o + mruby)の供養をする記事

ISUCON2015予選お疲れさまでした。
予選関連エントリとしてはかなり出遅れてしまいましたが、自分がISUCONに向けて仕込んでいたものの、日の目を見なかったネタの供養をしたいと思います。 燃えろよ燃えろ。
(自宅でBeagleBoneBlackつかって動かしてたブログが消滅してたのでこっちで書くジャバよ。)

参加チーム

アルパカ三姉妹 です。パカー。
ISUCONへの参加が初めてなのは自分だけだったので、足を引っぱるまいと気負いが凄かったと反省。

仕込んでいたもの

以前からmatsumoto-rさんのブログエントリ(その1, その2)によって、試したい気分が高まっていたh2oのmruby拡張をネタに一発逆転を目論み、sinatraアプリの一部ルーティングをmrubyで書けるような何かを作ろうとしました。
結果、

github.com

github.com

の2つのmrbgemsと、これら+sinatraアプリを移植するために必要そうなmrbgemsを組みこんだh2oのフォーク

github.com

を作りました。

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!

に対し、ベンチマークツールwrkを使ってみます。

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く"や"し"い"