【发布时间】:2012-06-15 22:53:11
【问题描述】:
我一直在处理一段内存密集型代码。我试图通过手动实现缓存阻塞、sw 预取、循环展开等在单个内核内对其进行优化。即使缓存阻塞显着提高了性能。但是,当我引入循环展开时,性能会大幅下降。
我在所有测试用例中都使用带有编译器标志 -O2 和 -ipo 的 Intel icc 进行编译。
我的代码类似这样(3D 25-point stencil):
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)上进行循环展开并分别以展开因子 2、4、8 分别在 x、y、z 方向上展开时,我在所有 9 种情况下都会出现性能下降,即在方向上展开 2 x,在 y 方向上展开 2,在 z 方向上展开 2,在 x 方向上展开 4 ... 等等。 但是当我在最外层循环(维度 k)上执行循环展开 8 倍(2 和 4 倍)时,我得到了 v.good 性能改进,甚至比缓存阻塞更好。
我什至尝试使用 Intel Vtune 分析我的代码。瓶颈似乎主要是由于 1.LLC 未命中和 2.远程 DRAM 服务的 LLC 负载未命中。
我无法理解为什么展开最内层最快的循环会导致性能下降,而展开最外层、最慢的维度会带来性能提升。然而,后一种情况的这种改进是当我使用 icc 编译时使用 -O2 和 -ipo 时。
我不确定如何解释这些统计数据。有人可以帮助阐明这一点。
【问题讨论】:
-
顺便说一句:大概编译器正在将 kdydx 优化为一个 temp?
-
@Mitch Wheat:我在原始代码中考虑了 kdxdy 和 j*dx 使用通用子表达式消除。这更像是一个示例代码
-
您可以使用 SSE 指令来计算其中的一些吗?乍一看,它看起来很适合......
-
你能发布一个降级展开案例的简短源示例吗?我很想看看它,看看 C 表达式的变化是否会导致在部分循环计算中使用效率较低的汇编指令。
标签: c icc intel-vtune loop-unrolling stencils