私の質問は、C++とC#について同僚との議論を解決することです。
大量のUDPストリームを受信するサーバーを実装しました。このサーバーは、非同期ソケットを使用してC++で開発され、完了ポートを使用してI/Oをオーバーラップさせました。 5つのスレッドで5つの完了ポートを使用します。このサーバーは、パケットの損失やエラーなしに、ギガビットネットワークで500 Mbpsのスループットを簡単に処理できます(テストを500 Mbpsを超えてプッシュしませんでした)。
同じ種類のサーバーをC#で再実装しようとしましたが、同じ着信スループットに到達できませんでした。 ReceiveAsync
メソッドとSocketAsyncEventArgs
のプールを使用した非同期受信を使用して、受信呼び出しごとに新しいオブジェクトを作成するオーバーヘッドを回避しています。各SAEventArgs
にはバッファが設定されているため、受信ごとにメモリを割り当てる必要はありません。プールは非常に大きいため、100を超える受信要求をキューに入れることができます。このサーバーは、240Mbpsを超える着信スループットを処理できません。その制限を超えると、UDPストリームで一部のパケットが失われます。
私の質問はこれです:C++ソケットとC#ソケットを使用して同じパフォーマンスを期待する必要がありますか?私の意見では、.NETでメモリが正しく管理されていれば、同じパフォーマンスになるはずです。
副次的な質問:.NETソケットが内部でI/O完了ポートをどのように使用するかを説明する良い記事/リファレンスを知っている人はいますか?
.NETソケットが内部でI/O完了ポートをどのように使用するかを説明する良い記事/リファレンスを誰かが知っていますか?
唯一の参照は実装(つまり、Reflectorまたは他のAssemblyデコンパイラー)だと思います。これにより、all非同期IOはIO完了ポートを通過し、コールバックはIOスレッドで処理されます。プール(通常のスレッドプールとは別です)。
5つの完了ポートを使用する
すべてのIOをスレッドの単一のプールに処理する単一の完了ポートを使用し、プールごとに1つのスレッドで完了を処理することを期待します(ディスクを含む他のIOを非同期的に実行していると仮定します) )。
何らかの形の優先順位付けが行われている場合は、複数の完了ポートが理にかなっています。
私の質問はこれです:C++ソケットとC#ソケットを使用して同じパフォーマンスを期待する必要がありますか?
「...ソケットの使用」部分をどの程度狭く定義するかに応じて、「はい」または「いいえ」。非同期操作の開始から完了が完了ポートにポストされるまでの操作に関しては、大きな違いはないと思います(すべての処理はWin32 APIまたはWindowsカーネルで行われます)。
ただし、.NETランタイムが提供する安全性により、オーバーヘッドが追加されます。例えば。バッファの長さがチェックされ、デリゲートが検証されます。アプリケーションの制限がCPUの場合、これによって違いが生じる可能性があり、極端な場合、わずかな違いが簡単に加算されます。
また、.NETバージョンはGCのために一時停止することがあります(.NET 4.5は非同期収集を行うため、これは将来改善される予定です)。ゴミの蓄積を最小限に抑える手法があります(たとえば、オブジェクトを作成するのではなく再利用し、ボックス化を避けながら構造を利用します)。
結局、C++バージョンが機能し、パフォーマンスのニーズを満たしているのであれば、なぜ移植するのでしょうか。
コードをC++からC#に直接移植して、同じパフォーマンスを期待することはできません。 .NETは、メモリ管理(GC)と、コードの安全性の確認(境界チェックなど)に関して、C++よりもはるかに多くのことを行います。
すべてのIO操作(たとえば65535 x 500 = 32767500バイト))に1つの大きなバッファーを割り当ててから、各SocketAsyncEventArgs
(および送信操作)にチャンクを割り当てます。メモリCPUよりも安価です。バッファマネージャ/ファクトリを使用して、すべての接続とIO操作(Flyweightパターン)にチャンクを提供します。Microsoftは非同期の例でこれを行います。
Begin/EndメソッドとAsyncメソッドはどちらも、バックグラウンドでIO完了ポートを使用します。後者は、パフォーマンスを向上させるために、操作ごとにオブジェクトを割り当てる必要がありません。
私の推測では、.NETとC++は実際には異なることをしているため、同じパフォーマンスは見られません。 C++コードは安全ではないか、境界を確認していない可能性があります。また、処理せずにパケットを受信する能力を測定しているだけですか?または、スループットにはパケット処理時間が含まれていますか?もしそうなら、あなたがパケットを処理するために書いたかもしれないコードはそれほど効率的ではないかもしれません。
プロファイラーを使用して、最も時間が費やされている場所を確認し、それを最適化することをお勧めします。実際のソケットコードは非常にパフォーマンスが高いはずです。