2

Clojure ネットワーク アプリケーションがあります。基本的な構造は次のようになります。

  • サーバーにはLinkedBlockingQueueまたはArrayBlockingQueueが1つあります(両方を試しました)
  • 複数のスレッドがネットワーク接続を受け入れoffer、キューに対して動作します
  • take無限ループでキューから1 つのスレッドを取得し、取得した各アイテムで作業します

そして、take呼び出しで重大なパフォーマンスの問題に気付きました:

  • スレッドはoffer非常に速い速度でキューに送られ、キューはそれらすべてを非常に迅速に処理します
  • キューからの 1 つのワーカー スレッドtakeの速度が非常に遅い (の速度の 200 倍以上遅いoffer)
  • CPU 使用率が非常に低いため、ワーカーはまったくビジーではありません

キューを使用しない場合、ベンチマークの状況では、同じワークロードが CPU 使用率を最大化し、満足のいく速度で実行できます。

では、このシナリオで使用するのに最適なキューイング手法は何でしょうか?

これが私のコードです(100行未満)。

https://github.com/HouzuoGuo/Aurinko/blob/master/src/Aurinko/core.clj

編集、私の観察の詳細:

  • リクエスト処理速度のベンチマークを行ったところ、キューを使用せずに 1 秒あたり約 8,000 リクエストで動作しました。
  • サーバー プログラムがリクエストをキューに入れるときにデバッグ メッセージを出力し、リクエストの処理が終了すると別のメッセージを出力するようにしました。
  • 毎秒約 1,000 件のリクエストをサーバーに送信する簡単なクライアント プログラムを作成しました。
  • サーバーは時間内にすべての要求をキューに入れ、キューは何千もの要素の長さになります。
  • デバッグ メッセージによると、ワーカー (リクエスト プロセッサ) は 1 秒あたり約 150 リクエストしか処理していないようです。

編集:

みんなの助けに感謝します。ブロッキング キューがパフォーマンスの問題の原因ではないことを確認しました。アプリケーションにパフォーマンスのボトルネックは見つかりませんでしたが、どこかにあるはずです。

最終編集:

みんなありがとう。パフォーマンスのボトルネックは、ブロッキング キューではなく、ネットワーク IO が原因でした。

4

3 に答える 3

2

あなたは次のように述べています:「CPU使用率は非常に低いので、ワーカーはまったく忙しくありません」。また、「キューのブロックがパフォーマンスの問題の原因ではないことを確認しました。アプリケーションでパフォーマンスのボトルネックは見つかりませんでしたが、どこかにあるはずです。」

これらのステートメントの両方が当てはまる場合は、ワーカースレッドがI/Oの待機に多くの時間を費やしている可能性があります。もしそうなら、簡単な解決策があります:複数のワーカースレッドを実行してください!

または、他の同時実行のボトルネック(作業キューではない)がある可能性があります。

次のことを行ってみませんか。ワークキューに約1,000個のアイテムをプッシュする小さなテストプログラムを作成してから、ワーカースレッドで実行されるのと同じコードの実行を開始します。キューが空になると、終了するはずです。そのプログラムのプロファイルを作成します。(開発マシンにプロファイラーをセットアップしていますか?私はJIPを使用するのが好きです。)

于 2012-09-24T12:42:07.423 に答える
2

表示されている内容の最も可能性の高い説明は、キューが空take()で待機していることです。キューが空でない場合、非常に高速になる可能性があります。

clojureのパフォーマンスはJavaのパフォーマンスと似ていると思います。

public static void main(String... args) throws InterruptedException {
    int runs = 20000;
    BlockingQueue<Integer> queue = new ArrayBlockingQueue<Integer>(runs + 1);
    BlockingQueue<Integer> queue2 = new LinkedBlockingQueue<Integer>(runs + 1);
    for (int i = 0; i < 10; i++) {
        testQueue(runs, queue);
        testTake(runs, queue);
        testQueue(runs, queue2);
        testTake(runs, queue2);
    }
}

private static void testQueue(int runs, BlockingQueue<Integer> queue) {
    long start = System.nanoTime();
    for (int i = 0; i < runs; i++)
        queue.offer(1);
    long time = System.nanoTime() - start;
    System.out.printf(queue.getClass().getSimpleName() + ": Average time to offer was %,d ns%n", time / runs);
}

private static void testTake(int runs, BlockingQueue<Integer> queue) throws InterruptedException {
    long start = System.nanoTime();
    for (int i = 0; i < runs; i++)
        queue.take();
    long time = System.nanoTime() - start;
    System.out.printf(queue.getClass().getSimpleName() + ": Average time to take was %,d ns%n", time / runs);
}

最後に印刷します

ArrayBlockingQueue: Average time to offer was 34 ns
ArrayBlockingQueue: Average time to take was 39 ns
LinkedBlockingQueue: Average time to offer was 78 ns
LinkedBlockingQueue: Average time to take was 54 ns
于 2012-09-21T07:33:53.410 に答える
0

結果は LinkedBlocingQueue と ArrayBlockingQueue で同じですか? キューからの要素の提供と取得の効率は、これら 2 つのデータ構造の違いに基づいて異なります。

于 2012-09-21T07:28:54.853 に答える