3

この質問は、Java の最新バージョンに関するものです。

30 個のプロデューサー スレッドが文字列を抽象キューにプッシュします。1 つのライター スレッドが同じキューからポップし、5400 rpm HDD RAID アレイに存在するファイルに文字列を書き込みます。データは約 111 MBps の速度でプッシュされ、約 80 MBps の速度でポップ/書き込みされます。プログラムは 5600 秒間存続します。これは、約 176 GB のデータがキューに蓄積されるのに十分です。一方、私は合計 64 GB のメイン メモリに制限されています。

私の質問は次のとおりです。どのタイプのキューを使用すればよいですか?

これが私がこれまでに試したことです。

1) ArrayBlockingQueue. このバインドされたキューの問題は、配列の初期サイズに関係なく、配列がいっぱいになるとすぐにライブネスの問題が発生することです。実際、プログラムの開始から数秒後に、topアクティブなスレッドが 1 つだけ報告されます。プロファイリングにより、プロデューサー スレッドは平均して、ほとんどの時間をキューが解放されるのを待つことに費やしていることがわかります。これは、フェア アクセス ポリシー (コンストラクターの 2 番目の引数を true に設定) を使用するかどうかに関係ありません。

2) ConcurrentLinkedQueue. liveness に関する限り、この無制限のキューはより優れたパフォーマンスを発揮します。約 700 秒後にメモリが不足するまで、30 個すべてのプロデューサー スレッドがアクティブになります。しかし、64GB の制限を超えると、信じられないほど遅くなります。これはページングの問題によるものだと推測しますが、これを証明する実験は行っていません。

私は自分の状況から抜け出す方法を 2 つ予見しています。

1) SSD を購入します。うまくいけば、I/O レートの増加が役に立ちます。

2) ファイルに書き込む前に出力ストリームを圧縮します。

代替手段はありますか?上記のキューのいずれかが構築/使用される方法に何か欠けていますか? それらを使用するより賢い方法はありますか?Java Concurrency in Practice book では、制限されたキューが使い果たされるよりも速くいっぱいになる場合に備えて、いくつかの飽和ポリシー (セクション 8.3.3) を提案していますが、残念ながらそれらのどれも --- 中止、呼び出し元の実行、および 2 つの破棄です。ポリシー---私のシナリオに適用されます。

4

5 に答える 5

3

ボトルネックを探します。メモリを使い果たしたくないので、制限されたキューは絶対に理にかなっています。

消費者をより速くするようにしてください。プロファイリングして、最も多くの時間を費やす場所を調べます。ここでディスクに書き込むので、いくつかの考え:

  • あなたの問題に使用できますNIOか?(たぶんFileChannel#transferTo())
  • 必要なときだけ洗い流してください。
  • 十分な CPU 予約がある場合は、ストリームを圧縮しますか? (すでに述べたように)
  • 速度のためにディスクを最適化します (レイド キャッシュなど)。
  • より高速なディスク

@Flavioがすでに言ったように、生産者と消費者のパターンについては、そこに問題はなく、現在のようになるはずです。最終的には、最も遅い側が速度を制御します。

于 2013-10-05T08:58:02.877 に答える
2

ここで問題がわかりません。生産者と消費者の状況では、システムは常に遅い方の速度で進みます。プロデューサーがコンシューマーよりも速い場合、キューがいっぱいになるとコンシューマーの速度まで遅くなります。

プロデューサーの速度を落とすことができないという制約がある場合は、コンシューマーの速度を上げる方法を見つける必要があります。消費者のプロファイリングを行い (あまり派手に始めないSystem.nanoTime()でください。多くの場合、数回の呼び出しで十分な情報が得られます)、消費者が最も多くの時間を費やしている場所を確認し、そこから最適化を開始します。CPU のボトルネックがある場合は、アルゴリズムを改善したり、スレッドを追加したりできます。ディスクのボトルネックがある場合は、書き込みを減らして (圧縮することをお勧めします)、より高速なディスクを入手し、1 つではなく 2 つのディスクに書き込みます...

于 2013-10-05T08:24:01.023 に答える
1

なぜプロデューサーが 30 人もいるのですか。その番号は問題のドメインによって固定されていますか、それともあなたが選んだ番号ですか? 後者の場合、プロデューサーの数を、消費量よりもわずかに大きい合計レートで生産するまで減らし、ブロッキングキューを使用する必要があります(他の人が示唆しているように)。次に、他のリソース(メモリ、スレッド)の使用を最小限に抑えながら、パフォーマンスを制限する部分である消費者をビジー状態に保ちます。

于 2013-10-05T10:23:08.243 に答える
1

解決策は 2 つしかありません。サプライヤーを遅くするか、消費者を速くするかです。より遅いプロデューサは、制限されたキューを使用して、特に多くの方法で実行できます。コンシューマを高速化するには、https://www.google.ru/search?q=java+memory-mapped+fileを試してください。https://github.com/peter-lawrey/Java-Chronicleを見てください。

もう 1 つの方法は、文字列から書き込みバッファーを準備する作業から書き込みスレッドを解放することです。プロデューサー スレッドが文字列ではなく、準備完了のバッファーを発行できるようにします。制限された数のバッファーを使用します。たとえば、2*threadnumber=60 です。最初にすべてのバッファを割り当ててから、それらを再利用します。空のバッファにはキューを使用します。生成スレッドはそのキューからバッファーを取得し、それを埋めて書き込みキューに入れます。書き込みスレッドは、書き込みスレッドからバッファーを取得し、ディスクに書き込み、空のバッファー キューに入れます。

さらに別のアプローチは、非同期 I/Oを使用することです。プロデューサは、特別な書き込みスレッドなしで、書き込み操作を自分で開始します。完了ハンドラ は、使用済みバッファを空のバッファ キューに戻します。

于 2013-10-05T17:20:43.450 に答える
1

Java「キューの実装」によると、あなたに適した他のクラスがあります:

  • LinkedBlockingQueue
  • PriorityBlockingQueue
  • DelayQueue
  • 同期キュー
  • LinkedTransferQueue
  • TransferQueue

これらのクラスのパフォーマンスやメモリ使用量はわかりませんが、自分で試すことができます。

これがお役に立てば幸いです。

于 2013-10-05T08:19:11.187 に答える