1

私は集中的にメモリにバインドされているコードを扱ってきました。キャッシュ ブロック、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 を使用した場合です。

これらの統計の解釈方法がわかりません。誰かがこれに光を当てるのを助けることができますか.

4

1 に答える 1

1

これは、典型的な展開によって命令キャッシュ ミスが発生していることを強く示唆しています。現代のハードウェアの時代では、アンロールはコードの高速化を自動的に意味するものではなくなりました。各内部ループがキャッシュ ラインに収まる場合、パフォーマンスが向上します。

生成されたコードのサイズを制限するために手動でアンロールできる場合がありますが、これには、生成された機械語命令とその位置を調べて、ループが単一のキャッシュ ライン内にあることを確認する必要があります。通常、キャッシュ ラインの長さは 64 バイトで、64 バイト境界に配置されます。

外側のループには同じ効果はありません。これらは、アンロール レベルに関係なく、命令キャッシュの外にある可能性があります。これらを展開すると分岐が少なくなるため、パフォーマンスが向上します。

「リモート DRAM によって処理されるロード ミス」は、1 つの NUMA ノードにメモリを割り当てたが、現在は別のノードで実行していることを意味します。NUMA に基づいてプロセスまたはスレッドのアフィニティを設定することが答えです。

私が使用した Intel マシンでは、リモート DRAM の読み取りにローカル DRAM のほぼ 2 倍の時間がかかります。

于 2012-12-16T17:54:17.790 に答える