HTTP/2とSPDY及びHTTP/1.1の実装におけるコンテンツサイズの変化による性能の遷移について

前回のエントリ「軽量な静的コンテンツ配信におけるHTTP/2とSPDYのWebサーバの性能を見てみよう」では、非常に小さな静的コンテンツに対してのレスポンス性能を比較しました。今回はより詳細な比較を見るために、リクエストするコンテンツサイズの変化によって、性能がどのように遷移するかを測定し、それを測定してグラフ化しました。(ローカルホストでベンチかけてるのは環境が十分にないからなので遺憾ながら家の単一VMで評価しています。)

ベンチマーク結果

ベンチマーク測定は前回のエントリと同様の環境と設定で行っており、リクエストするコンテンツファイルのサイズを前回の21byteから20000byte(20kbyte)まで遷移させて、それぞれのコンテンツサイズで、HTTP/1.1のnginx、SPDY/3.1のnginx、HTTP/2のnghttpdの1秒間のリクエスト処理性能がどのように変化するかをグラフ化しました。前回と同様、負荷のパラメータは同時接続数100、総接続数10万でSPDYやHTTP/2においては、単一セッションでの並列ストリーム数を100、HTTP/1.1はkeepalive有りです。

以下、結果のグラフになります。

http11spdy31http2bench

このように、結果を見るとリクエストするコンテンツのファイルサイズが増えると共に赤色グラフのSPDY/3.1のnginxと緑色グラフのHTTP/2のnghttpdの性能が劣化していく一方で、HTTP/1.1の従来のnginxの性能はほぼ変わりませんでした。また6000byte(6kbyte)を超えた辺りから、HTTP/1.1のnginxよりもSPYD/3.1やHTTP/2のnghttpdの性能が下回り、その後はファイルサイズが増えるに従って性能が低下していきました。

簡単な考察

このような結果になった考察はまだきちんとできていないのが本音です。また、ローカルホスト向けにベンチマークをかけている事もあるので、きちんとした考察はできていませんが、ざっと思いついた事を書こうと思います。もしこの結果をみて何か思う事があれば、はてブやTwitterで感想や考察を書いて頂けるとうれしいです。

まず、これらがプロトコルの仕様による性能の差なのかどうかという点ではまだ判断つきません。というのも、SPDY/3.1のnginxもexperimentのようですし、nghttpdもまだ開発が始まった段階です。という前提を元に簡単に実装を見てみました。

まず、HTTP/1.1のnginxはコンテンツサイズが変わっても、前回のエントリのnginxのチューニングにより、TCP_CORKオプションによってTCPスタックに余計な事をさせずに、ヘッダ情報をwritev()した上でsendfile()で一気にコンテンツデータを送っています。この間呼ばれるシステムコールは、今回の評価におけるファイルサイズの変化に関わらず、setsockopt()、writev()、sendfile()、close()と非常に少ないです。

また、SPDY/3.1のnginxの実装では、pread()とwrite()を行い、コンテンツサイズが30Kbyteを超えだすと、bufferで複数回pread()とwrite()を呼び出すようになります。このような実装になっているのは、SSLの場合TCPソケットにデータを書く前にセッション層で暗号化しておく必要があり、そのままsendfileによってfdをコピーできないからでしょうか。

一方で、HTTP/2のnghttpd実装では、常にbufferの4086byteでread()を行い、さらにwrite()も細切れにしてデータを書き込んでいます。これはHTTP/2の実装だからこうならざるを得ないのかは分かっていませんが、これによって呼ばれるシステムコールの数は、コンテンツサイズが増えるごとにHTTP/1.1のnginxの数倍に膨れ上がっている事が分かります。その結果、大きなコンテンツサイズに対して10万ものリクエストを投げると、呼ばれるreadやwrite系のシステムコールの数は大きく膨れ上がってしまうように思われます。しかし、SPDY同様、SSLだからこういう実装になっていると考えられます。

ただし、SPDYに関してはbufferに分ける段階の30Kbyte未満のコンテンツにおいて既に性能が劣化していたため、システムコールの数ではなく、例えばTCP_CORK+writev()+sendfile()とpread()+write()の性能差(TCPスタックに余計な事をさせない、Nagleアルゴリズムとか)なのか、といった考察が改めて必要そうです。いずれにしても、30Kbyte以上のファイルはbufferを分けて複数回pread()、write()を行うようになります。

以上により、前回のエントリと比較して、非常に軽量なコンテンツサイズであれば、呼ばれるシステムコールの数や処理性能に差が出ないという理由により、単一のセッションで並行ストリームにより同時並列的に処理できるSPDY/3.1やHTTP/2が高速に処理出来たと予想します。一方で、コンテンツサイズが増えるとその並行処理よりも呼ばれるシステムコールの数やpread()やwrite()性能がボトルネックとなって、HTTP/1.1のnginxがTCP_CORK+wirtev()+sendfile()でデータを送っているのに対して遅くなってしまう、ということでしょうか。

まとめ

今回は、前回のエントリより少し踏み込んで、リクエストするコンテンツファイルのサイズを変化させながら、各プロトコルを実装したWebサーバのレスポンスの特徴とその実装を比較しました。SPDYやHTTP/2の細かい実装上の都合をきちんと理解していないのでこのような表面的な考察になってしまいましたが、引き続き調査していきたいと思います。もし読んでいてお気づきの点がありましたら、色々とお聞かせ下さい。

まだ先の技術ではありますが、我々エンジニアにとっては避けて通れない道のように思うので、今のうちから情報共有できたらうれしいです。