【问题标题】:Dependency chain analysis依赖链分析
【发布时间】:2016-08-12 20:31:50
【问题描述】:

来自Agner Fog's "Optimizing Assembly" guide,第 12.7 节:循环示例。讨论示例代码的段落之一:

[...] Pentium M 分析:... 13 uops,每时钟 3 次 = 每 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之前执行。


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

【问题讨论】:

  • 比较/分支对将被预测,因此它并不真正计数。除此之外,我不确定你在问什么
  • 能否请您链接 Agner 的文档并说明您引用的部分和示例?

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


【解决方案1】:
  1. mul 不是 loop-carried 依赖链的一部分,因此可以同时存在来自多个迭代的mulpd insns。单条指令的延迟根本不是这里的问题,而是依赖。每次迭代都有一个 单独的 13c load、mulpd、subpd、store 依赖链。乱序执行允许来自多个迭代的微指令同时运行。

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

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

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


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

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

【讨论】:

  • 谢谢:)。首先要说明的是:好的,很明显,多个迭代可以同时进行。但是,当它只涉及一次迭代时,特别是迭代。为什么mulpd(5个周期)的延迟无关紧要?毕竟subpd xmm0, xmm1后面必须跟mulpd xmm1, xmm2(在一个依赖链中进行一次迭代)。对不起我的英语,我知道这可能有问题。 2. Agner Fog 说 add eax, 16 是循环携带的,它需要 1 个循环(延迟)。
  • @Gilgamesz:2. 没错。 add -> add 是循环携带的依赖链,而不是add -> cmp -> jl -> add
  • re:第一点:您能否更具体地说明您为什么认为它确实很重要?我们计算的是吞吐量,而不是单次迭代的延迟。只要乱序的 insn 调度器和 ReOrder Buffer 足够大以暴露迭代之间的并行性,迭代中的 dep 链的延迟就无关紧要了。 (一个非常长的 dep 链需要一个大的调度程序和 ROB)。该 dep 链中任何特定指令的延迟甚至更不相关。
  • 好的,@Peter Cordes 有道理。非常感谢。 :)
猜你喜欢
  • 2019-06-06
  • 2010-12-18
  • 2014-05-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-08-18
  • 2012-12-02
  • 1970-01-01
相关资源
最近更新 更多