依赖链分析

时间:2016-04-20 09:16:01

标签: performance assembly x86 cpu-architecture micro-optimization

Agner Fog's "Optimizing Assembly" guide开始,第12.7节:循环示例。讨论示例代码的其中一段:

  

[...] Pentium M的分析:......每时钟3个微处理器13个微处理器=每4.33c退休时间一次迭代。

     

循环中有一个依赖链。延迟时间为:2为   存储器读取,5表示乘法,3表示减法,3表示存储器   写入,总共13个时钟周期。这是原来的三倍   退休时间,但它不是一个循环携带的依赖,因为   每次迭代的结果都保存到内存中,不会重复使用   下一次迭代。乱序执行机制和   流水线操作使得每次计算都可以在之前开始   完成上述计算。唯一的循环携带   依赖链是add eax,16,其延迟只有1。

## Example 12.6b.  DAXPY algorithm, 32-bit mode
[...]   ; not shown: initialize some regs before the loop
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

我无法理解为什么依赖链不会增加整个吞吐量。我知道找到最严重的瓶颈很重要。在考虑依赖链之前确定的最严重的瓶颈是融合域uop吞吐量,每次迭代4.33个周期。我无法理解为什么依赖链不是一个比这更大的瓶颈。

  1. 我看到作者解释说它与无序执行和流水线有关但我看不到它。我的意思是,只有乘法才会导致延迟5个周期,所以只有这个值大于4个周期。

  2. 我也无法理解作者为什么不关心这里的依赖: add eax, 16 -> cmp eax, ecx -> jl L1 毕竟,必须在cmp之前执行添加,cmp之前必须执行jl

  3. PS:后面的段落确定了Pentium M作为解码的最大瓶颈,将其限制为每6c一次迭代,因为128b矢量操作解码为每个两个uop。有关Core2,FMA4 Bulldozer和Sandybridge的其余分析,分析和调整,请参阅Agner Fog指南。

1 个答案:

答案 0 :(得分:2)

  1. mul不是循环携带依赖关系链的一部分,因此可以同时在多个迭代中进行mulpd个insn。单个指令的延迟根本不是问题,它是依赖。每次迭代都有一个单独的 13c依赖性加载链,mulpd,subpd,store。乱序执行允许多次迭代的uops同时在飞行中。

  2. 每次迭代中的cmp / jl取决于该迭代中的add,但下一次迭代中的add不依赖于cmp。推测执行和分支预测意味着控制依赖(条件分支和间接跳转/调用)数据依赖链的一部分。这就是为什么一次迭代的指令可以在前一次迭代的jl退出之前开始运行的原因。

    相比之下,cmov 数据依赖而不是控件依赖,因此无分支循环往往具有循环携带的依赖链。如果分支预测良好,这往往比分支慢。

    每个循环迭代都有一个单独的cmp / jl依赖链,就像FP依赖链一样。

  3.   

    我无法理解为什么依赖链不会增加整个吞吐量。

    我不知道这句话是什么意思。我想我能够找出你所有其他混淆的单词和短语。 (例如“链依赖”而不是“依赖链”。)看看我对你的问题的编辑;其中一些也可能有助于你理解。