0

シリアル化された多数のJavaオブジェクトから本質的に構成されるバイナリファイルのセットを読み取るコードがあります。スレッドプール内のファイルの読み取りを実行して、コードを並列化しようとしています(Executors.newFixedThreadPool

私が見ているのは、スレッド化された場合、読み取りは実際には単一のスレッドよりも遅くなります。スレッドの数に応じて、1.5倍から10倍遅くなります。

私のテストケースでは、実際には複数のスレッドから同じファイル(35mb)を読み取っているので、I/Oに縛られることはありません。私はCPUよりも多くのスレッドを実行しておらず、プール間の同期もありません。つまり、大量のファイルを独立して処理しているだけです。

スレッド化されたときにこのパフォーマンスが低下する理由として考えられるものを誰かが知っていますか?何を探すべきですか?または、問題を分析するための最良の方法は何ですか?スレッド間で共有できるクラス内の静的変数をすでに探しましたが、何も表示されません。java.*スレッドでインスタンス化されたときにクラスの1つが大幅に遅くなる可能性がありますか(たとえばjava.zip.deflate、私が使用しているもの)?
ヒントをありがとう。

Upd:もう1つの興味深いヒントは、単一のスレッドを実行している場合、読み取りを行う関数の実行時間は一定から高精度ですが、複数のスレッドを実行している場合、タイミングに大きなばらつきが見られることです。

4

3 に答える 3

2

同じジョブjava.zip.deflateを実行する複数のスレッドを追加すると、35MBの読み取りがより高速に実行されることを期待しているように思えます。そうではありません。実際、IOバウンドではないかもしれませんが、追加するスレッドごとにカーネルオーバーヘッドが発生します(バッファコピーなど)。カーネルバッファスペースから完全に読み取っている場合でも、CPUと処理のオーバーヘッドが発生します。

とはいえ、1.5倍から10倍遅くなることに驚いています。各処理スレッドが出力を書き込んでいる場合、明らかにそれはキャッシュされません。

ただし、メモリの競合が発生している可能性があります。Javaシリアル化オブジェクトストリームを処理している場合は、頻繁にリセットしない限り、メモリ消費量を監視する必要があります。シリアル化により、オブジェクトへの参照が多数保持されるため、連続する大規模なストリームで大量のGC帯域幅を生成できます。

jconsoleを使用してプログラムに接続し、[メモリ]タブを注意深く監視します。サバイバーと旧世代のスペースがいっぱいになると、CPUに非線形の影響が見られます。

于 2012-05-15T15:44:57.177 に答える
0

すべてのスレッドワーカーが同じファイルから読み取っているからといって、それがIOバウンドではないことを確実に意味するわけではありません。それは可能性があります。そうではないかもしれません。確かに、すべてのスレッドワーカーがメモリ内のファイルとディスク外のファイルから読み取るようにテストケースを設定します。

OSがファイルをキャッシュしたと思いますが、ファイルが読み取り専用/共有モードで開かれているかどうかを確実に知っていますか?そうでない場合でも、OSはファイルをロックして、一度に1つのスレッドのみがアクセスできるようにすることができます。

潜在的に関連するリンク:

于 2012-05-15T15:45:01.187 に答える
0

この問題は、実際には多くの同期メソッドを持つjava.util.zip.Inflateクラスが原因で発生しました(それらのいくつかはネイティブコードを使用するため)。したがって、複数のスレッドが実行されている場合、同期メソッドは互いに競合し、コードを作成します。シーケンシャルに非常に近い。

解決策は、java.util.zipクラスをGNUクラスパスのJavaのみのバージョンに置き換えることでした(例:ここからhttp://git.savannah.gnu.org/cgit/classpath.git/tree/java/util/zip

于 2012-05-16T02:51:12.603 に答える