【问题标题】:Understanding matrix multiply on Intel Xeon PHi 7210了解 Intel Xeon PHi 7210 上的矩阵乘法
【发布时间】:2020-09-10 02:34:27
【问题描述】:

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

  1. 硬件:英特尔至强融核处理器 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,但如果在共享服务器上运行,则可能会从指令缓存中逐出一些迭代,从而导致性能下降。

【问题讨论】:

    标签: performance benchmarking cpu-architecture cpu-cache xeon-phi


    【解决方案1】:

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

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

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


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

    我还建议您使用 perf 或 VTune 等工具分析您的代码,这些工具使您能够分析 硬件事件(例如 L1/L2 缓存操作)并确认/拒绝挥霍金钱的假设,并帮助您改进此代码。

    【讨论】:

    • 我添加了一个解释,解释了为什么 in2[k*dim + j] 在内部循环中如此糟糕,尤其是对于 2 矩阵大小的幂。由于 OP 一开始就有这个问题,他们可能不知道它为什么不好。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-01-02
    • 1970-01-01
    • 1970-01-01
    • 2017-03-16
    • 1970-01-01
    相关资源
    最近更新 更多