依赖链分析

从Agner Fog的“优化装配”指南,第12.7节:循环示例。 讨论示例代码的段落之一:

对Pentium M的分析:......每个时钟3个13个uops =每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个周期。 我不明白为什么依赖链不是比那更大的瓶颈。

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

  • 我也不明白为什么作者不关心这里的依赖关系: add eax, 16 -> cmp eax, ecx -> jl L1毕竟,必须在jl之前执行cmpcmp之前执行加法。


  • PS:稍后的段落指出了Pentium M解码的最大瓶颈,将其限制为每6c一次迭代,因为128b向量操作会解码为两个uops。 请参阅Agner Fog的指南以了解其他分析,以及Core2,FMA4推土机和Sandybridge的分析+调整。


  • 的MUL不是循环搬运依存链的一部分,因此可以有mulpd在飞行中从多个迭代的insn一次。 单条指令的延迟根本就不是问题,它是依赖链。 每次迭代都有一个独立的13c依赖链,包括load,mulpd,subpd,store。 无序执行是允许来自多个迭代的uops一次在飞行中。

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

    相比之下, cmov是数据依赖关系而不是控制依赖关系,因此无cmov循环倾向于具有循环承载的依赖关系链。 如果分支预测得好,这往往比分支慢。

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


  • 我不明白为什么依赖链不会增加整个吞吐量。

    我不知道这句话的意思。 我想我能弄清楚所有其他的混合词和措辞。 (例如,“链依赖”而不是“依赖链”)。看看我对你的问题的编辑; 其中一些也可能有助于你的理解。

    链接地址: http://www.djcxy.com/p/28785.html

    上一篇: Dependency chain analysis

    下一篇: How can I write a QuadWord from AVX512 register zmm26 to the rax register?