これはstackoverflowに関する私の最初の投稿です...誰かが私を助けてくれることを願っています
Java 6 LinkedBlockingQueue
で大きなパフォーマンスの低下があります。最初のスレッドでいくつかのオブジェクトを生成し、それをキューにプッシュします。2番目のスレッドでこれらのオブジェクトを引き出します。パフォーマンスの低下は、LinkedBlockingQueue
のtake()
メソッドが頻繁に呼び出されると発生します。プログラム全体を監視したところ、take()
メソッドが全体として最も多くの時間を要求しました。 〜58Mb/sから0.9Mb/sになります...
キューのポップメソッドとテイクメソッドは、このクラスの静的メソッドで呼び出されます
_public class C_myMessageQueue {
private static final LinkedBlockingQueue<C_myMessageObject> x_queue = new LinkedBlockingQueue<C_myMessageObject>( 50000 );
/**
* @param message
* @throws InterruptedException
* @throws NullPointerException
*/
public static void addMyMessage( C_myMessageObject message )
throws InterruptedException, NullPointerException {
x_queue.put( message );
}
/**
* @return Die erste message der MesseageQueue
* @throws InterruptedException
*/
public static C_myMessageObject getMyMessage() throws InterruptedException {
return x_queue.take();
}
}
_
take()
メソッドを調整して少なくとも25Mb/sを達成するにはどうすればよいですか、または「キュー」がいっぱいまたは空のときにブロックする他のクラスを使用できますか。
敬具
バート
追伸:英語が下手でごめんなさい、私はドイツ出身です;)
プロデューサースレッドは単純にコンシューマーが消費するよりも多くの要素を配置しますであるため、キューは最終的に容量制限に達し、プロデューサーは待機します。
今から私の元の答えを統合すると、基本的に全体像がわかります。
put()
sを実行することにより、LinkedBlockingQueue
(すべてのキューに1つあります)の固有のスループット制限に達します。この場合、継続的なtake()s
でさえ、それ以上の処理はありません。 (ちなみに、これは、この構造では、とにかくJVMとマシンで、put()が読み取りよりも少なくともわずかにコストがかかることを示しています)。SynchronousQueue
、ConcurrentLinkedQueue
、およびjsr166yの今後のTransferQueue
を試すことができます。いくつかの提案:
/ジョンW.が私の元の答えが誤解を招くと正しく指摘した後に更新されました
通常、パフォーマンスに敏感なコード領域ではLinkedBlockingQueueを使用せず、ArrayBlockingQueueを使用することをお勧めします。 LinkedBlockingQueueよりもはるかに優れたガベージコレクションプロファイルを提供し、キャッシュに適しています。
ArrayBlockingQueueを試して、パフォーマンスを測定してください。
LinkedBlockingQueueの唯一の利点は、無制限にできることですが、これが実際に必要なことはめったにありません。コンシューマーに障害が発生し、キューのバックアップが開始される場合、キューが制限されていると、システムが正常に機能しなくなり、キューが制限されていない場合に発生する可能性のあるOutOfMemoryErrorsのリスクが発生します。
試してみることがいくつかあります。
LinkedBlockingQueue
をArrayBlockingQueue
に置き換えます。ぶら下がっている参照がないため、キューがいっぱいになったときの動作が向上します。具体的には、LinkedBlockingQueueの1.6実装では、キューが実際に空になるまで、要素の完全なGCは発生しません。
プロデューサー側が常にコンシューマー側のパフォーマンスを上回っている場合は、drain
またはdrainTo
を使用して「バルク」テイク操作を実行することを検討してください。
または、キューにメッセージオブジェクトの配列またはリストを取得させます。プロデューサーはリストまたは配列にメッセージオブジェクトを入力し、各putまたはtakeは、同じロックオーバーヘッドで複数のメッセージを移動します。秘書が「外出中」のメッセージのスタックを渡すのではなく、一度に1つずつ渡すと考えてください。
見つかりました この興味深い投稿 キューサイズとガベージコレクションによるパフォーマンスの問題について。
充填プロセスについて何も知らずに何が起こるかを言うのは難しいです。
addMyMessage
の呼び出し頻度が低い場合(おそらくアプリケーションのまったく異なる部分でのパフォーマンスの問題が原因)、take
メソッドは待機する必要があります。
そうすれば、take
が原因のように見えますが、実際にはそれがアプリケーションの重要な部分です。
ブロッキングキューからオブジェクトを出し入れするための生のパフォーマンスオーバーヘッドがボトルネックである場合( 遅い生産者/消費者問題 ではない)、オブジェクトのバッチ処理でパフォーマンスを大幅に向上させることができます。たとえば、代わりにきめの細かいオブジェクトを配置または取得する場合は、オブジェクトの粗粒度のリストを配置または取得します。コードスニペットは次のとおりです。
ArrayBlockingQueue<List<Object>> Q = new ArrayBlockingQueue<List<Object>>();
// producer side
List<Object> l = new ArrayList<Object>();
for (int i=0; i<100; i++) {
l.add(i); // your initialization here
}
Q.put(l);
// consumer side
List<Object> l2 = Q.take();
// do something
バッチ処理により、パフォーマンスが1桁向上します。
確かに何も言えません。ただし、(実験と同じように)BlockingQueue
の実装を変更してみることができます。
初期容量を50kに設定し、LinkedBlockingQueue
を使用します。同じ容量でArrayBlockingQueue
を試してください。また、fair
パラメーターで遊ぶこともできます。
アプリケーションがJava 6のロック関連の変更、特に「バイアスロック」機能の影響を受ける可能性があります。
-XX:-UseBiasedLocking
スイッチを使用して無効にしてみて、違いが生じるかどうかを確認してください。
詳細については、これを参照してください: http://Java.Sun.com/performance/reference/whitepapers/6_performance.html