こんにちは仲間のプログラマー。すでに1 つの質問をしましたが、非常に良い回答が得られたにもかかわらず、問題を解決できませんでした。次に、時間をかけてコードをリファクタリングし、並列化の可能性を向上させました (計算バッチを減らし、それぞれの計算負荷を高くすることにより)。しかし、それでもシリアル処理よりも優れたパフォーマンスは得られません。
この並列処理の遅さは、コンテキストの切り替えによるものと思われます。または、共通オブジェクトの「自動」同期が原因である可能性があります。何が起こっているのかを理解するのを手伝ってくれると思います。
私の場合を述べさせてください。私は科学計算用のプログラムを作成しています。外部のものに依存するのではなく、開始時に与える入力値に依存します。この問題のサイズはNs
(私が使用する名前です) で測定できます。これは解の「解像度」と見なすことができ、ユーザー入力の 1 つであり、通常は 100 程度です。
このように、メイン クラスに doubleys[Ns][N]
やなどの double 配列がいくつかありますphiS[Ns][Nord][N]
。ここで、N と Nord はプログラムの他の固定された大きさです。私のプログラムでは、Ns
ポイントごとにいくつかのことを計算する必要があり、ここで並列化が行われます。各点の計算は独立しているので、それらを別のスレッドに分割して、より高速になることを願っています。
そこで、ループを作成する代わりに、for (int i=0; i<Ns; <i++)
この計算義務を Runnable バッチに分割しました。それぞれのバッチの範囲は、より小さな間隔: ですfor (int i=start; i<end; i++)
。ここで、開始と終了は常に 0 から Ns の間です。たとえば、デュアル コア PC を使用している場合、2 つのバッチを作成します。1 つはstart = 0
とend = Ns/2
で、もう1 つはstart = Ns/2
とend = Ns
です。私がクアッドコアを使用している場合、2番目のバッチも必要start = Ns/4
にend = Ns/2
なります(すべてのケースで分割が正確であると仮定して)。
Runnable を実装するクラスとしての各 Batch は、 に格納され、コアの数に等しいサイズArrayList<Batch>
の に与えられます。FixedThreadPool
バッチを実行し、単純なCountDown
スキームを使用して終了するのを待ちます。
このバッチのそれぞれは、プログラムのメイン クラスからこれらの配列のデータにアクセスする必要がありますが、それらのアクセスは、各バッチが からのみ読み取るyS[start][]
ためyS[end][]
、2 つのバッチが同じ配列要素を読み取ろうとすることは決してありません。各バッチが他のバッチと同じ要素にアクセスしようとしていないとしても、Java はまだ yS をロックするのではないかと思います。
私の問題は、各バッチが何千もの double を処理する必要があるため、コンテキストの切り替えによるオーバーヘッドに関連しているのか、プログラムの構築方法がそれに影響を与える可能性があるのか も疑問に思います。
関連する配列の要素だけを各バッチに渡す方法を見つける必要があるかもしれませんが、これにアプローチする方法はわかりません。ポインターがあれば、単純なポインター操作で、何も再割り当てすることなく、必要な要素だけの新しい配列を作成できます。Javaでそのようなことを行う方法はありますか?
最後に、同期が必要なコードの一部 (他の配列を処理する部分) があり、それは既に正常に動作しています。上記で説明したこの計算の義務は、私のプログラムが行う唯一のことではありません。それらはループ内にあり、順次処理部分と交互になっていますが、合計実行時間としては非常に重要です。
要約すると、質問は次のとおりです。期待していたのに、なぜマルチスレッドで得られないのですか?
ここでプレーンシリアルとマルチスレッドプログラムを数回実行したところ、シリアルで14500ミリ秒、マルチスレッドで15651ミリ秒になりました。どちらも同じデュアル コア上にあります。その他の注意事項: シリアル実行では、各計算デューティ (0 から Ns) に約 1.1 から 4.5 ms かかります。デュアル スレッドから、各バッチ (Ns/2 ポイント) には約 0.5 ~ 3 ミリ秒かかります。(run()メソッドの上から下まで計測。数値収束の仕方により毎回の計算負荷が異なります)
ご清聴ありがとうございました。