私は集中的にメモリにバインドされているコードを扱ってきました。キャッシュ ブロック、sw プリフェッチ、ループ展開などを手動で実装することにより、シングル コア内で最適化しようとしています。キャッシュ ブロックによってパフォーマンスが大幅に向上しますが。ただし、ループ展開を導入すると、パフォーマンスが大幅に低下します。
すべてのテスト ケースで、コンパイラ フラグ -O2 および -ipo を使用して Intel icc でコンパイルしています。
私のコードはこれに似ています(3D 25ポイントステンシル):
void stencil_baseline (double *V, double *U, int dx, int dy, int dz, double c0, double c1, double c2, double c3, double c4)
{
int i, j, k;
for (k = 4; k < dz-4; k++)
{
for (j = 4; j < dy-4; j++)
{
//x-direction
for (i = 4; i < dx-4; i++)
{
U[k*dy*dx+j*dx+i] = (c0 * (V[k*dy*dx+j*dx+i]) //center
+ c1 * (V[k*dy*dx+j*dx+(i-1)] + V[k*dy*dx+j*dx+(i+1)])
+ c2 * (V[k*dy*dx+j*dx+(i-2)] + V[k*dy*dx+j*dx+(i+2)])
+ c3 * (V[k*dy*dx+j*dx+(i-3)] + V[k*dy*dx+j*dx+(i+3)])
+ c4 * (V[k*dy*dx+j*dx+(i-4)] + V[k*dy*dx+j*dx+(i+4)]));
}
//y-direction
for (i = 4; i < dx-4; i++)
{
U[k*dy*dx+j*dx+i] += (c1 * (V[k*dy*dx+(j-1)*dx+i] + V[k*dy*dx+(j+1)*dx+i])
+ c2 * (V[k*dy*dx+(j-2)*dx+i] + V[k*dy*dx+(j+2)*dx+i])
+ c3 * (V[k*dy*dx+(j-3)*dx+i] + V[k*dy*dx+(j+3)*dx+i])
+ c4 * (V[k*dy*dx+(j-4)*dx+i] + V[k*dy*dx+(j+4)*dx+i]));
}
//z-direction
for (i = 4; i < dx-4; i++)
{
U[k*dy*dx+j*dx+i] += (c1 * (V[(k-1)*dy*dx+j*dx+i] + V[(k+1)*dy*dx+j*dx+i])
+ c2 * (V[(k-2)*dy*dx+j*dx+i] + V[(k+2)*dy*dx+j*dx+i])
+ c3 * (V[(k-3)*dy*dx+j*dx+i] + V[(k+3)*dy*dx+j*dx+i])
+ c4 * (V[(k-4)*dy*dx+j*dx+i] + V[(k+4)*dy*dx+j*dx+i]));
}
}
}
}
最も内側のループ (次元 i) でループ展開を行い、x、y、z 方向にそれぞれ展開係数 2、4、8 で展開すると、9 つのケースすべてでパフォーマンスが低下します。つまり、方向 x で 2 ずつ展開し、展開します。 y 方向に 2 ずつ展開、z 方向に 2 展開、x 方向に 4 展開 ... など。キャッシュのブロックよりも優れた v.good パフォーマンスの向上を実現します。
Intel Vtune を使用してコードのプロファイリングも試みました。リモート DRAM によってサービスされる 1.LLC ミスと 2.LLC ロード ミスが主な原因のボトルネックのように見えました。
最も内側の最速のループを展開するとパフォーマンスが低下するのに、最も外側の最も遅い次元を展開するとパフォーマンスが向上する理由を理解できません。ただし、後者の場合のこの改善は、icc でコンパイルするときに -O2 と -ipo を使用した場合です。
これらの統計の解釈方法がわかりません。誰かがこれに光を当てるのを助けることができますか.