質問

boost :: asioを使用してC ++でミニHTTPサーバーを開発しましたが、今では複数のクライアントで負荷テストを行っていますが、CPUの飽和状態に近づくことができませんでした。 Amazon EC2インスタンスでテストしていますが、1つのCPUの使用率が約50%、別のCPUの使用率が20%で、残りの2つはアイドル状態です(htopによる)。

詳細:

  • サーバーはコアごとに1つのスレッドを起動します
  • リクエストが受信、解析、処理され、レスポンスが書き出されます
  • リクエストはデータ用であり、メモリから読み取られます(このテストでは読み取り専用)
  • 2つのマシンを使用してサーバーを「ロード」しています。それぞれがJavaアプリケーションを実行し、25スレッドを実行し、リクエストを送信しています
  • 約230リクエスト/秒のスループットが見られます(これは application リクエストで、多くのHTTPリクエストで構成されています)

では、この結果を改善するにはどうすればよいですか? CPUの大部分はアイドル状態であるため、この追加の容量を活用して、800リクエスト/秒などのスループットを向上させたいと思います。

これまでのアイデア:

  • リクエストは非常に小さく、数ミリ秒で満たされることが多いので、クライアントを変更してより大きなリクエストを送信/作成することができます(おそらくバッチ処理を使用)
  • HTTPサーバーを変更してSelectデザインパターンを使用できますが、これはここで適切ですか?
  • ボトルネックとは何かを理解するために、プロファイリングを行うことができます
役に立ちましたか?

解決

boost :: asioは期待するほどスレッドフレンドリーではありません-boost / asio / detail / epoll_reactor.hppのepollコードの周りには大きなロックがあります。つまり、カーネルのepoll syscallを呼び出すことができるスレッドは1つだけです。一度に。また、非常に小さなリクエストの場合、これによりすべての違いが生じます(つまり、おおよそシングルスレッドのパフォーマンスのみが表示されます)。

これは、boost :: asioがLinuxカーネル機能を使用する方法の制限であり、必ずしもLinuxカーネル自体ではないことに注意してください。 epoll syscallは、エッジトリガーイベントを使用する場合、複数のスレッドをサポートしますが、(過度のロックなしで)正しく取得することは非常に難しい場合があります。

ところで、私はこの分野でいくつかの作業を行っており(完全にマルチスレッド化されたエッジトリガーepollイベントループとユーザーがスケジュールしたスレッド/ファイバーを組み合わせています)、 nginetd プロジェクト。

他のヒント

EC2を使用しているため、すべてのベットはオフになっています。

実際のハードウェアを使用して試してみると、何が起こっているのかを見ることができるかもしれません。 VMでパフォーマンステストを試みることは基本的に不可能です。

EC2の有用性についてはまだ解決していません。誰かが見つけたら、教えてください。

ネットワーク使用率に関するコメントから、
ネットワークの動きはあまりないようです。

3 + 2.5 MiB / sec は、(1Gbpsポートと比較して) 50Mbps ボールパーク程度です。

次の2つの問題のいずれかが発生していると思います。

  1. 不十分なワークロード(クライアントからの低いリクエスト率)
    • サーバーのブロック(干渉応答の生成)

cmeerw のメモとCPU使用率の数値を見る
50%+ 20%+ 0%+ 0%でアイドル)
サーバー実装の制限である可能性があります。
2番目の cmeerw の答え(+1)。

230リクエスト/秒は、このような単純な非同期リクエストでは非常に低いようです。そのため、複数のスレッドの使用はおそらく時期尚早な最適化です。適切に動作させ、単一のスレッドで調整し、まだ必要かどうかを確認してください。不要なロックを取り除くだけで、速度が上がる可能性があります。

この記事には、WebサーバーのI / O戦略に関する詳細と議論があります。 2003年頃のスタイルスタイルのパフォーマンス。

ASIOは、小規模から中規模のタスクには適していますが、基盤となるシステムの能力を活用するのにはあまり適していません。生のソケット呼び出しも、WindowsのIOCPでもありませんが、経験があれば、常にASIOよりも優れています。いずれにせよ、これらのすべてのメソッドには多くのオーバーヘッドがあり、ASIOにはさらに多くのオーバーヘッドがあります。

価値があるもの。カスタムHTTPでrawソケットコールを使用すると、4コアI7で毎秒800Kの動的リクエストを処理できます。そのレベルのパフォーマンスのために必要な場所は、RAMから提供されています。このレベルのパフォーマンスでは、ネットワークドライバーとOSがCPUの約40%を消費しています。 ASIOを使用すると、1秒あたり約50〜100Kのリクエストを受け取ることができます。そのパフォーマンスは非常に可変であり、ほとんどがアプリでバインドされています。 @cmeerwによる投稿のほとんどがその理由を説明しています。

パフォーマンスを改善する1つの方法は、UDPプロキシを実装することです。 HTTPリクエストをインターセプトし、UDPを介してバックエンドUDP-HTTPサーバーにルーティングすることで、オペレーティングシステムスタックで多くのTCPオーバーヘッドをバイパスできます。また、UDP自体でパイプスルーするフロントエンドを使用することもできますが、これは難しくありません。 HTTP-UDPプロキシの利点は、変更せずに適切なフロントエンドを使用でき、影響なしで自由に交換できることです。それを実装するには、さらに2、3台のサーバーが必要です。この例の変更により、OSのCPU使用率が10%に低下し、その1つのバックエンドで1秒あたりの要求が100万を超えました。 FWIWフロントエンドは、より重要な動的リクエストバックエンドの速度を落とすことなくデータをキャッシュできるため、パフォーマンスサイトには常にフロントエンドとバックエンドのセットアップが必要です。

将来は、独自のネットワークスタックを実装する独自のドライバーを作成し、可能な限りリクエストに近づき、独自のプロトコルを実装できるようにしているようです。それはおそらくより複雑なので、ほとんどのプログラマーが聞きたいことではないでしょう。私の場合、CPUを40%使用して、1秒あたり100万を超える動的要求に移行できます。 UDPプロキシメソッドを使用すると、これを行うことなく最適なパフォーマンスに近づけることができますが、より多くのサーバーが必要になります。いくつかの軽量UDPプロキシはそれほど大した問題ではありません。

このうちのいくつかがあなたに役立つことを願っています。

io_serviceのインスタンスはいくつありますか? Boost asioには、 example があります。 CPUごとにio_serviceを使用し、RoundRobinのように使用します。

引き続き4つのスレッドを作成してCPUごとに1つ割り当てることができますが、各スレッドは独自のio_serviceでポーリングできます。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top