周期太多了

时间:2016-04-22 18:57:04

标签: optimization x86

我考虑来自Agner Fog的优化装配的一个例子。他测试:

例12.6b。 DAXPY算法,32位模式

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个循环。而且我不知道我是否在理论计算中犯了错误,或者它可以改进。

我做了什么:

enter image description here

来自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

1 个答案:

答案 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个循环中发出,这没有任何意义。