以下のコードでは、OpenMP の標準parallel for
節を使用して並列化しています。
#pragma omp parallel for private(i, j, k, d_equ) shared(cells, tmp_cells, params)
for(i=0; i<some_large_value; i++)
{
for(j=0; j<some_large_value; j++)
{
....
// Some operations performed over here which are using private variables
....
// Accessing a shared array is causing False Sharing
for(k=0; k<10; k++)
{
cells[i * width + j].speeds[k] = some_calculation(i, j, k, cells);
}
}
}
これにより、ランタイムが大幅に改善されました (~140 秒から~40 秒) が、実際に遅れていることに気付いた領域がまだ 1 つあります。それは、上記でマークした最も内側のループです。
上記の配列が False Sharing を引き起こしていることは確かです。なぜなら、以下の変更を加えると、パフォーマンスがさらに大幅に向上する (~40 秒から ~13 秒) ためです。
for(k=0; k<10; k++)
{
double somevalue = some_calculation(i, j);
}
つまり、メモリの場所をプライベート変数に書き込むように変更するとすぐに、速度が大幅に向上しました。
今説明したシナリオで False Sharing を回避することでランタイムを改善する方法はありますか? 問題自体は何度も言及されていますが、この問題に役立つと思われる多くのリソースをオンラインで見つけることができないようです.
非常に大きな配列 (必要なものの 10 倍) を作成して、各要素の間に十分なマージン スペースを確保し、キャッシュ ラインに入ったときに他のスレッドがそれを取得しないようにするというアイデアがありました。ただし、これは目的の効果を生み出すことができませんでした。
そのループで見つかった False Sharing を削減または削除する簡単な (または必要に応じて難しい) 方法はありますか?
どんな形の洞察や助けも大歓迎です!
EDIT : some_calculation() が次のことを行うと仮定します:
(tmp_cells[ii*params.nx + jj].speeds[kk] + params.omega * (d_equ[kk] - tmp_cells[ii*params.nx + jj].speeds[kk]));
反復ごとに計算される d_equ に依存しているため、この計算を for ループの外に移動することはできません。