私は、2Dおよび3Dで地球内の変形のシミュレーションを行うために使用する、パーティクルインセルコードの並列化に取り組んでいます。コードのいくつかのルーチンは、OpenMPを使用して並列化するのが簡単で、非常にうまくスケーリングできます。ただし、粒子からグリッドセルへの補間を処理するコードの重要な部分で問題が発生しました。パーティクルは、反復ごとに動き回っています(速度フィールドに従って)。多くの計算は、通常の変形しないグリッドで実行するのに最も効率的です。したがって、各反復には、「ランダムに」分散された粒子からグリッドセルへの通信が含まれます。
この問題は、次の簡略化された1Dコードで説明できます。
//EXPLANATION OF VARIABLES (all previously allocated and initialized, 1D arrays)
//double *markerval; // Size Nm. Particle values. Are to be interpolated to the grid
//double *grid; // Size Ng=Nm/100 Grid values.
//uint *markerpos; // Size Nm. Position of particles relative to grid (each particle
// knows what grid cell it belongs to) possible values are 0,1,...Ng-1
//#pragma omp parallel for schedule(static) private(e)
for (e=0; e<Nm; e++) {
//#pragma omp atomic
grid[markerpos[e]]+=markerval[e];
}
最悪のシナリオでは、パーティクルの位置はランダムですが、通常、メモリ内で互いに隣接しているパーティクルは、空間内でも、したがってグリッドメモリ内でも互いに隣接しています。
この手順を効率的に並列化するにはどうすればよいですか?複数の粒子が同じグリッドセルにマッピングされるため、上記のループが直接並列化されている場合、競合状態とキャッシュスワッピングの可能性はゼロではありません。更新をアトミックにすることで競合状態を防ぎますが、コードはシーケンシャルの場合よりもはるかに遅くなります。
また、各スレッドのグリッド値のプライベートコピーを作成し、後でそれらを合計しようとしました。ただし、これにはおそらく使用するコードに多すぎるメモリが必要であり、この例では、スレッドの数に応じて十分に拡張できませんでした(理由は不明です)。
3番目のオプションは、グリッドからパーティクルにマップしてから、パーティクルインデックスの代わりにグリッドインデックスをループすることです。ただし、これは非常に複雑で、コードを大幅に変更する必要があります。また、計算コストもかかるソートルーチンを使用する必要があるため、どれだけ役立つかはわかりません。
誰かがこれまたは同様の問題の経験を持っていますか?