Apache2.4から正式にデフォルトのMPMになったevent_mpmですが、いまいち良さを実感する事ができていませんでした。たいしてパフォーマンスも出ないし、これ本当に意味あるの?という人が多かったかもしれません。
そこで、マルチプロセッシング周りを真面目にチューニングしてみようと思い、あれこれ試してみると、ある設定値において急にevent_mpmが本気を出し始め、すごいベンチマークスコアを叩きだしたので、それらをまとめようと思います。正直びっくりしています
今回の比較ではキャッシュ等のチューニングは行わず、マルチプロセッシング周りのみのチューニングを行って、純粋なリクエスト処理のアーキテクチャの性能比較を行いました。
同時接続数固定の最高処理能力比較
これまでは、数十バイトの静的コンテンツを単純に処理する場合、同時接続数を100総接続数10万ぐらいのベンチでNginxよりApacheが性能を出した事を見たことはありませんでした。ブログ等でもあまり見かけた事はありません。それほどNginxの独壇場だったように思います。この分野はNginxの圧勝だろうと思って…いた時期が僕にもありました。
実際にevent_mpmの設定を多くのパターンを試して、以下の設定をしたときに、とんでもない性能が発揮されたのです。
まずは、event_mpmのデフォルトの設定(インストール時)は以下のようになっていますね。
MinSpareThreads 75
MaxSpareThreads 250
ThreadsPerChild 25
MaxRequestWorkers 400
MaxConnectionsPerChild 0
events {
worker_connections 1024;
}
- コマンド
- 結果
Apache2.4.3 | Nginx1.2.3 | |
Requests/sec | 10746.47 | 14708.75 |
はい、これは僕がいつも良く見るApacheとNginxの性能差に見えます。大体、ApacheはNginxの75%程度の性能に落ち着きます。数十バイトの静的コンテンツに対するリクエスト処理はNginxの得意分野だと思っていたので、大体こんなものです。 そこで、真面目にevent_mpmのチューニングを行ってみました。で、幾度となくベンチを試した結果導き出した、静的コンテンツに対する同時接続数100程度に対して最高のパフォーマンスを示すevent_mpmの設定は以下のようになりました。
MinSpareThreads 4
MaxSpareThreads 4
ThreadsPerChild 2
MaxRequestWorkers 2
MaxConnectionsPerChild 0
events {
worker_connections 1024;
}
- 結果
Apache2.4.3 | Nginx1.2.3 | |
Requests/sec | 18682.31 | 17021.03 |
おお、abコマンドのベンチによる静的コンテンツの処理で、ApacheがNginxを上回っているのをはじめてみた気がします。Apacheのevetn_mpmも適切なチューニングによっては、上記のような値が得られる事が分かりました。これはびっくりです。 上記で述べたMaxRequestWorkersとThreadsPerChildの値を例えば4とかに変えると、以下のような値になります。
Apache2.4.3 | Nginx1.2.3 | |
Requests/sec | 13777.59 | 17021.03 |
MaxRequestWorkersとThreadsPerChildの値を1にしてみると以下のような値になります。
Apache2.4.3 | Nginx1.2.3 | |
Requests/sec | 9058.07 | 17021.03 |
MaxRequestWorkersを4、ThreadsPerChildの値を1にしてみると以下のような値になります。
Apache2.4.3 | Nginx1.2.3 | |
Requests/sec | 12949.39 | 17021.03 |
このように、これらの値をWebサーバへのアクセスパターンに合わせてやることで、大きく性能に変化が生じます。event_mpmにおいてはとても大事な値だとわかりました。
同時接続数の変化における性能比較
上記では、Webサーバへのアクセスパターンが例えば同時接続数100ぐらいだと仮定して、それに合わせた最適なチューニングで性能比較を行いました。次に、Nginxやevent_mpmの売りである、同時接続数が1000のオーダーになった時に耐えられるチューニングを行い、性能を評価しました。 まずは、openfile数の制限を変更します。
119 #define MAX_SERVER_LIMIT 60000
120 #endif
MaxRequestWorkers 60000
StartServers 4
MinSpareThreads 4
MaxSpareThreads 4
ThreadsPerChild 2
MaxConnectionsPerChild 0
events {
worker_connections 60000;
}

[pid 24120] poll([{fd=11, events=POLLIN}], 1, 60000 <unfinished ...>
[pid 24121] poll([{fd=7, events=POLLIN}], 1, 60000 <unfinished ...>
[pid 24122] futex(0x9550d68, FUTEX_WAIT_PRIVATE, 695987, NULL <unfinished ...>
MaxRequestWorkers 60000
ThreadLimit 1000
StartServers 1000
MinSpareThreads 1000
MaxSpareThreads 1000
ThreadsPerChild 100
MaxConnectionsPerChild 0
追記
twitter上で@kfujieda さんにアドバイスを頂きつつ、再度eventの同時接続数20000あたりをさばけるような設定を検討しました。event_mpmといえども、nginxのようにそれぞれのworkerが複数のリクエストを同時に処理できている、つまりコアの数だけスレッドを用意すれば良いわけではなく、多重スレッド、マルチスレッド、マルチプロセスを組み合わせたようなMPMのアーキテクチャであることが分かってきました。 つまり、nginxとは違って、きちんとスレッドの数とプロセスの数、及び、同時接続数の数などの組み合わせをきちんと考慮する必要があり、スレッドやプロセスに依存する面があると考えられます。詳しくはマニュアルをご覧下さい。 以上を踏まえて、@kfujiedaさんのJAISTでのworker_mpmにおける設定も考慮しつつ、コア4つのメモリ8G環境で、同時接続数を万のオーダーで処理できるような設定を検討した結果、以下のような設定になりました。
ServerLimit 32
MaxRequestWorkers 8192
ThreadLimit 256
StartServers 2048
MinSpareThreads 2048
MaxSpareThreads 2048
ThreadsPerChild 256
MaxConnectionsPerChild 0
nginxとApacheのeventはかなり良い勝負ができている事がわかります。Apache的にはかなり良い結果なのではないかと思われます。
最後に
今回はevent_mpmをワークロードに最適化したチューニングを行えば、ある条件下ではNginxよりも早くなる事がわかりました。これは、これまであまり見かけない情報だったので、少し未来を感じる検証結果になったように思えます。
また、追記にも書いた通り、大量の同時接続数を想定したチューニングを行っておけば、nginxと同じような性能を出せる事も分かってきました。ワークロードで細かく設定したい場合はeventを使うという選択肢もでてきたかもしれません。
しかし、多重I/Oのブロック周りで負荷をかけるとevent_mpmは問題をかかえているように思えます。ここさえクリアになれば、安心してかなり使えるマルチプロセッシングモジュールになるのではないかと思います。
いずれにせよ、MaxRequestWorkersとThreadsPerChildのチューニングがevent_mpmを使う上での肝になる事は明らかだと言えるでしょう。この設定方法が、今後のevent_mpmのチューニングの参考になれば良いなと思っています。
Nginxの設定に関して勉強したい方はまずは以下の本がよいでしょう。
Apacheの設定に関しては以下がおすすめです。
7 Comments.