2

LinuxでUDPC++サーバーアプリを作成し、負荷テストを行って、処理できるクライアントの数を確認しています。毎秒2〜4の速度でパケットを送信する約150の同時クライアントでピークに達することがわかりました。

その後に追加されたクライアントにより、他のクライアントのパケットがドロップされます。

サーバー自体にストレスがかからず、CPUとメモリの10%未満しか使用していません。ネットワークにも、約15Kバイト/秒のストレスはまったくありません。パケットは、約200パケット/秒でサーバー(読み取りと書き込みの両方に1つのUDPソケットを使用)に到着します。サーバースレッド自体は、この負荷レベルで短時間スリープします。

質問
ここでのボトルネックは何ですか?CPU、ネットワーク、サーバーコード自体はすべてストレスがないようです。OSはこの数のUDPパケットを処理できませんか?

ハードウェアは非常に低電力で、1.5MHzでシングルコアのPentiumに相当します。NICは1億ビット/秒です。Ubuntu11.1を実行しています。

この記事は関連している可能性があります:WindowsServer2008でのUDPパフォーマンスの上限

更新:サーバーはUDPソケットをセットアップしてから、3つのスレッドと2つのキューを作成します。ソケット読み取りの最初のスレッドブロックは次のようになります。

while (1)
{
    recvfrom(this->socket, readBuf, BUFSIZE, 0, (sockaddr *)&address, &addressLen);
    pushBack(this->inputQueue, message);
}

2番目のスレッドはinputQueueでスリープします。状態が通知されるとウェイクアップし、メッセージを処理します。処理されたメッセージをoutputQueueに送信します。

while (1)
{
    sleepOnQ(this->inputQueue);
    popFront(this->inputQueue);
    processMessage();
    pushBack(this->outputQueue, message);
}

3番目のスレッドはoutputQueueでスリープし、UDPソケットから宛先にメッセージを送信します。読み取りに使用されるのと同じソケットであることに注意してください。

while (1)
{
    sleepOnQ(this->outputQueue);
    popFront(this->outputQueue);
    processMessage();
    sendto(this->socket, message, ... );
}

クライアントおよびメッセージごとの処理量は少ないです。サーバーが毎秒200メッセージを処理しているときに述べたように、サーバーは本当に弱々しいCPUの約10%を使用しています。

システムのカーネルパラメータの一部を次に示します。

net.core.wmem_max = 114688
net.core.rmem_max = 114688
net.core.wmem_default = 114688
net.core.rmem_default = 114688

データ同期に関する詳細情報

これまでの答えから、2つのことが起こっていると思いました。

  1. OS読み取りバッファがいっぱいになっています。しかし、CPUが低い場合、これは発生しないはずです
  2. ただし、スレッドが他のイベントを待機している場合、#1が発生する可能性があります。そのため、ソケットを十分に速く読み取れません。

ロギングが問題になる可能性があります。ログをオフにして結果を報告してみます。ただし、おそらくもっと重要なのは、スレッド間のキューの競合です。CPUが低いため、スレッドはキューへのアクセスを待機するのに多くの時間を費やしている可能性があります。

このサーバーの最初の反復では、データのロックについて注意を払うようにしました。サーバーは非常に高速でしたが、800パケット/秒に達するとクラッシュしました。現在のバージョンでは、キュー全体がロックされます。たぶん私はスレッドを同期するためのより良い方法が必要です。

質問に答えました

ここで得た情報はとても役に立ちました。問題はテストクライアントの骨頭エラーでしたが、調査を行うことで、ここで提案された原因を取り除くことができました。

参考までに、これが私の結果です。クライアントの問題を修正すると、サーバーは70%のCPU使用率で約800パケット/秒を受け入れました。OSの読み取り/書き込みバッファを128Kから12MBに増やしました。読み取りバッファーがいっぱいかどうかはテストしませんでした。最高速度では、サーバーの読み取りスレッドが10回または20回の読み取りごとに短時間読み取りをブロックしていたため、OS読み取りバッファーが問題であったとは思えません。

800パケット/秒はまだ遅すぎるので、サーバーからログを削除しました。これは大きな違いを生みました。サーバーは、70%のCPUユーティリティで1400以上のクライアントから2900メッセージ/秒を受信できました。

また、読み取りスレッドがロックを待機しているかどうかをテストしました。最高速度でも、1ミリ秒以上待つ必要がないことがわかったので、2900メッセージ/秒の要因ではありませんでした。おそらくそれはより高速なCPU上にあるでしょう。

この時点で、サーバーはCPUにバインドされており、次のボトルネックを見つけるには、より強力なCPUを使用する必要があります。ご協力いただきありがとうございます!

4

2 に答える 2

1

着信UDPデータグラムがUDP入力バッファに適合しない場合(通常はバッファがいっぱいです)、カーネルはそれを破棄します。

パケットレートがわずか200/sで、CPU負荷が低いときにバッファがいっぱいになると、プログラムは新しいパケット以外の何か(スリープの終了、リソースなど)を待つために時間を浪費します。

コードを再確認してください。sleepそして、すべての、nanosleepおよび同様の睡眠機能を取り除くようにしてください。

シリアルコンソールに大量の(デバッグ)出力を出力すると、シリアルポートがそれほど高速ではないため、プログラムがブロックされ始める可能性があります。この種のボトルネックも解消してください。

于 2012-12-06T09:41:47.847 に答える
1

損失の最も可能性の高い理由は、最初のスレッドが空にする前に、UDP ソケットの受信パケット バッファーがいっぱいになることです。バッファがすでに満杯のときに受信した着信 UDP パケットはドロップされます。

最初のスレッドがバッファーを空にできない理由として最も可能性が高いのは、バッファーがいっぱいにならないようにするために、他の何かが長時間 CPU からバッファーを引き離しているということです。コア CPU の場合、これが該当する可能性があります。2 番目と 3 番目のスレッドの優先度を低く設定して (競合が発生した場合に最初のスレッドが CPU で最初の dib を取得するように)、それが役立つかどうかを確認してみてください。それでも十分でない場合は、プロセスを SCHED_RR の「リアルタイム」優先度に設定して、OS で実行されている他のプロセスが最初のスレッドを CPU から遠ざけないようにすることができます。(もちろん、他のスレッドがいつ実行されるかはそれほど重要ではないため、他のスレッドをより低い優先度で実行することもできます)。

于 2012-12-06T07:33:36.867 に答える