了解英特尔至强 PHi 7210 上的矩阵乘法

Understanding matrix multiply on Intel Xeon PHi 7210

我有以下工作程序可以正确生成结果,但是我对一些统计数据感到困惑。设置如下:

  1. 硬件:Intel Xeon Phi 处理器 7210
  2. 软件:两个 NxN 矩阵的乘法(在我的例子中是 512x512)
  3. 数据结构:所有 3 个矩阵都分配到高带宽内存中(即在 16GB mcdram 中)

密码是:

void MatrixMultiply(img_in1, img_in2, img_out, myRank, nRanks)
{
    for (int i=startingRow ; i<endingRow ; i++)     //no of rows of first matrix divided on per process basis
    {
        for (int j=0 ; j<M1Cdim ; j++)          //no of cols of first matrix
        {
            int temp = 0;
            for(int k=0 ; k<M2Rdim ; k++)       //no of rows of second matrix
            {
            temp += in1[i*M1Rdim+k] *  in2[k*M2Rdim+j]; //out(i,j) +=  in1(i,k) * in2(k,j)
        }
        out[i*M1Rdim+j] = temp;
    }
    }
}

main()
{
  for (int i = 1; i <= 10; i++) 
  {
    MPI_Barrier(MPI_COMM_WORLD);
    const double t0 = omp_get_wtime();
    MatrixMultiply(img_in1, img_in2, img_out, myRank, nRanks);
    MPI_Barrier(MPI_COMM_WORLD);
    const double t1 = omp_get_wtime();

    const double ts   = t1-t0;      // time in seconds
    const double tms  = ts*1.0e3;   // time in milliseconds
    const double gbps = double(sizeof(P)*2*img_in1.height*img_in1.width*img_in2.height)*1e-9/ts;    // bandwidth in GB/s
    const double fpps = double(2*img_in1.height*img_in1.width*img_in2.height)*1e-9/ts;          // performance in GFLOP/s

    if (myRank == 0) 
    {   
      printf("%5d %15.3f %15.3f %15.3f %s\n", i, tms, gbps, fpps);
    }
  }
}

The statistics I am getting are:

 Step        Time, ms            GB/s         GFLOP/s
    1           2.306         116.408         116.408
    2           2.334         115.017         115.017
    3           2.297         116.855         116.855
    4           2.295         116.964         116.964 
    5          16.692          16.082          16.082 
    6          11.468          23.407          23.407 
    7           2.299         116.758         116.758 
    8           2.291         117.171         117.171 
    9           2.295         116.964         116.964 
   10          10.792          24.874          24.874 

所以我的问题是:

为什么第 5、6 和 10 次迭代显示的结果比其他迭代更差?

我怀疑即使数据放在高带宽内存 (mcdram) 中,但代码本身是从缓存中执行的,因此可能会受到攻击。尽管整个程序非常小,只有 54KB,但如果 运行 在共享服务器上,则可能会从指令缓存中逐出一些迭代,从而导致性能下降。

这个矩阵乘法代码效率很低

确实,in2[k*M2Rdim+j]行很可能会导致缓存抖动,因此计算时序的高度不稳定如果行必须经常从 MCD-RAM 重新加载。虽然 MCD-RAM 具有高带宽,但它也具有高延迟(类似于 DDR-RAM 之一)。在这种情况下,延迟可能是一个大问题。

具体来说,沿着矩阵的一列向下跨步对于空间局部性来说是很糟糕的。更糟糕的是,当矩阵维度是 2 的幂时:您可能会在缓存中出现冲突未命中,因为所有这些缓存行都将别名到集合关联缓存中的同一集合。即使工作集很小,这也可能导致缓存抖动。


因此,请使用BLAS函数(来自MKL、OpenBLAS、ATLAS等)!它们远比这更优化。如果不能,请考虑改进此代码。你可以找到一个很好的解释你这样做 here。我认为超过 10 的加速是很容易实现的。

我还建议您使用 perf 或 VTune 等工具分析 您的代码,这些工具使您能够分析硬件事件(例如L1/L2 缓存操作)和 confirm/reject 现金交易假设以及帮助您改进此代码。