了解英特尔至强 PHi 7210 上的矩阵乘法
Understanding matrix multiply on Intel Xeon PHi 7210
我有以下工作程序可以正确生成结果,但是我对一些统计数据感到困惑。设置如下:
- 硬件:Intel Xeon Phi 处理器 7210
- 软件:两个 NxN 矩阵的乘法(在我的例子中是 512x512)
- 数据结构:所有 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 现金交易假设以及帮助您改进此代码。
我有以下工作程序可以正确生成结果,但是我对一些统计数据感到困惑。设置如下:
- 硬件:Intel Xeon Phi 处理器 7210
- 软件:两个 NxN 矩阵的乘法(在我的例子中是 512x512)
- 数据结构:所有 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 现金交易假设以及帮助您改进此代码。