我考虑来自Agner Fog的优化装配的一个例子。他测试:
L1:
movapd xmm1, [esi+eax] ; X[i], X[i+1]
mulpd xmm1, xmm2 ; X[i] * DA, X[i+1] * DA
movapd xmm0, [edi+eax] ; Y[i], Y[i+1]
subpd xmm0, xmm1 ; Y[i]-X[i]*DA, Y[i+1]-X[i+1]*DA
movapd [edi+eax], xmm0 ; Store result
add eax, 16 ; Add size of two elements to index
cmp eax, ecx ; Compare with n*8
jl L1 ; Loop back
他估计Pentium M上有6个循环/迭代。我试着在我的CPU-Ivy Bridge上做同样的事情。我每次迭代实现了3个循环,但从我在纸上的计算中可以得到2个循环。而且我不知道我是否在理论计算中犯了错误,或者它可以改进。
我做了什么:
来自http://www.realworldtech.com/sandy-bridge/5/我知道我的CPU可以退出4微秒进行循环,因此它不是瓶颈。
#uops fused = 8 / 4 = 2
所以2
是我们当前的瓶颈。让我们看看另一个可能性:
微型具有图案:1-1-1-1-1-1-1-1,根据Agner Fog,我的CPU具有图1-1-1-1(和其他)。由此可以看出,可以在2个周期内解码指令。这不是瓶颈。此外,SnB cpus具有微型存储器,因此取出和解码都不应成为瓶颈。
以字节为单位的指令大小为32,因此它适合微缓存窗口(32字节)。
从我的实验中,当我添加nop
指令时,它会增加每次迭代的周期数(约0.5个周期)。
所以,问题是:
那个循环在哪里? :d
答案 0 :(得分:1)
您确定不受内存带宽的限制吗?您需要使用适合L1缓存的数组进行测试,因为您依赖于两个负载和每2个时钟一个存储。 (这超过了IvB的一半,每个时钟最多有两个128b内存操作,其中最多只有一个是商店。)
解码无关紧要,因为你的循环适合循环缓冲区。 (它不到28微博)。所以它只发布了4个已经解码过的uops组。
您的融合域uop计数错误。 cmp/jl
可以宏融合成一个比较和分支的uop。然而,这个错误被另一个错误所平衡,原因是Agner Fog指南中还没有这个错误。
movapd [edi+eax], xmm0
无法在IvB上微融合,因此它有2个融合域uops。
SnB-family CPUs can only micro-fuse memory operands that don't use an index register.我最近在英特尔的优化手册中找到了官方确认,该手册解释了Agner测试与我测试的不同结果:这种寻址模式可以在解码器和uop中微融合缓存,但不在OOO核心。 (请参阅我对该问题的回答。我应该向Agner发送另一条消息,让他知道英特尔的文档解决了我们的困惑......)
试试这个:
add ecx, edi ; end-of-Y pointer
sub esi, edi ; esi = X-Y
L1:
movapd xmm1, [esi+edi] ; 1 uop
mulpd xmm1, xmm2 ; 1 uop
movapd xmm0, [edi] ; 1 uop
subpd xmm0, xmm1 ; 1 uop
movapd [edi], xmm0 ; 1 micro-fused uop
add edi, 16 ; 1 uop
cmp edi, ecx ; loop while (dst < end_Y)
jb L1 ; cmp+jb = 1 macro-fused uop
负载不需要微融合,但存储是2个融合域uop。 (商店地址和商店数据)。
IACA会告诉你这家商店是2 uops并且不能微熔。值得一看。值得一看。有时它的数字是错误的(例如,它认为shrd
在SnB上仍然很慢),但只要你意识到它与真实硬件行为的简单近似,它通常是有用的,并且不是一个周期精确的模拟器。
我的版本是7个完整的融合域uops。所以它应该在SnB系列CPU上每2个时钟运行一次。你的原作是8 uops,所以这个改变不应该有任何区别。我在注意到你没有考虑到cmp / jcc的宏观融合之前写了它,所以我认为你的循环实际上是9 uops。由于添加单个nop
会降低您的代码速度,因此可以进一步证明它有8个融合域uop。如果使用过大的数组进行测试的缓存未命中并不能解释它,那么IvB可能会以某种方式调度加载/存储uops吗?似乎不太可能,因为它们都必须使用端口2或3来存储地址或加载微操作。 (在未融合的域中,store-data uops转到port4)。
你确定你的循环每3个循环确实有一次迭代吗?添加nop
可以减慢速度,因为9 uop循环应该在3个循环中发出,这没有任何意义。