我正在尝试在L1高速缓存中获取完整带宽,以用于Intel处理器上的以下功能
float triad(float *x, float *y, float *z, const int n) {
float k = 3.14159f;
for(int i=0; i<n; i++) {
z[i] = x[i] + k*y[i];
}
}
这是来自STREAM的三合会功能。
使用具有此功能的SandyBridge / IvyBridge处理器(与NASM配合使用),可以得到约95%的峰。但是,使用Haswell只能达到峰值的62%,除非我展开循环。如果我解开16次,我将得到92%。我不明白
我决定使用NASM在汇编中编写函数。汇编中的主循环如下所示。
.L2:
vmovaps ymm1, [rdi+rax]
vfmadd231ps ymm1, ymm2, [rsi+rax]
vmovaps [rdx+rax], ymm1
add rax, 32
jne .L2
结果在Agner Fog的“优化组装”手册中的示例12.7-12.11中,他y[i] = y[i] +k*x[i]对Pentium M,Core 2,Sandy Bridge,FMA4和FMA3 所做的几乎相同(但针对)。我设法自己或多或少地复制了他的代码(实际上,当他广播时,他在FMA3示例中有一个小错误)。他在表中提供了每个处理器(FMA4和FMA3除外)的指令大小计数,融合操作,执行端口。我尝试自己为FMA3制作这张桌子。
ports
size μops-fused 0 1 2 3 4 5 6 7
vmovaps 5 1 ½ ½
vfmadd231ps 6 1 ½ ½ ½ ½
vmovaps 5 1 1 1
add 4 ½ ½
jne 2 ½ ½
--------------------------------------------------------------
total 22 4 ½ ½ 1 1 1 0 1 1
大小是指指令长度(以字节为单位)。add和jne指令之所以只有半个μop,是因为它们被融合为一个宏操作(不要与仍然使用多个端口的μop融合相混淆),并且只需要端口6和一个μop。 该vfmadd231ps指令可以使用端口0或端口1。我选择端口0。负载vmovaps可以使用端口2或3。我选择2并vfmadd231ps使用端口3。为了与Agner Fog的表格保持一致,并且由于我认为说一条可以平均分配到不同端口的指令每次使用的时间都是1/2更为合理,因此我为端口分配了1/2 vmovaps并vmadd231ps可以至。
根据该表以及所有Core2处理器每个时钟周期都可以执行4μop的事实,看来该循环应该在每个时钟周期都可行,但我还没有设法获得它。有人可以向我解释为什么我不能不展开就无法接近Haswell上此功能的峰值带宽吗?如果不展开就可以吗?如果可以,怎么办?让我清楚一点,我实际上是在尝试为此功能最大化ILP(我不仅想要最大的带宽),所以这就是我不想展开的原因。
潇湘沐
相关分类