monoの開発ブログ

WebサーバでのThundering Herdは過去の話?

nginx 1.9.1の紹介記事を読んでいて、昔悩んでいたThundering Herdの話を思い出したので書いてみます。

Thundering Herdというのは、ひとつのソケットに対してselectepollのような通信可能になるのを待つシステムコールを利用して複数のプロセス (またはスレッド) が待機していると、通信可能になったときに本来ならひとつのプロセスだけが起きればいいところを、待機していたすべてのプロセスが起こされてしまうという問題です。この場合、実際に通信できるのはたまたま最も早く通信を開始したプロセスのみで、他のプロセスが起きたことは無駄になってしまいます。

SO_REUSEPORTを使うと、ひとつのソケットに対してひとつのプロセスだけが通信可能になるのを待つことができ、カーネルは同じポートを見ているソケットの中からひとつだけを選択してくれるようになります。これにより、無駄にプロセスが起こされることは一切なくなり、性能向上が期待できるというわけです。

それはそれで大変よいことなのですが、実際のところThundering Herdがいうほど性能を低下させているのかどうかというのが個人的に疑問なのです。

もし、1プロセスが1リクエストしか捌けないモデルであれば、大量のプロセスが無駄に起こされることになって掛かるCPU時間もバカにならないでしょう。しかし、nginxのように1プロセスが大量のリクエストを捌くモデルであれば、プロセス数は高々コア数であり、プロセスを起こす負荷はそれほど大きくならないのではないかと感じます。しかも、ある程度サーバに対してアクセスが集中していて、起こされるプロセス数をキューに溜まった接続要求数が上回っていれば、たとえThundering Herdの問題で言うところの無駄に起こされたプロセスであったとしても、acceptに成功してしまって結果的には起こされたことは無駄ではなかったという現象も生じ得ます。

そう考えると、紹介記事ではTundering Herdに言及していますが、Thundering Herdの解消で性能が2倍になるというのはいまいち納得できません。

どちらかと言うと、SO_REUSEPORTが解決してくれる問題のうち、記事で言及されていない点のほうが大きいように思います。詳しくはこのあたりで説明されていますが、カーネル内でのソケットの表現がひとつだとロックの取り合いが起きるところを、各プロセスが別のソケットを扱うことで取り合わなくて済むようになったり、異なるコアに配置されるであろう複数のプロセスが同じアドレスにアクセスしてCPUのキャッシュが効果的に働かなくなる問題を防げるといった利点があります。

ただ、それでも2倍になるのはよくわからないですねぇ。36コアだからスケーラビリティの改善が大きく響くのかとも思いましたが、4 NGINX workersとあるのでそういうわけでもなさそうですし。この辺の理由ってどうやったら特定できるんですかねぇ。