来自Agner Fog's "Optimizing Assembly" guide,第12.7节:循环示例。其中一段讨论的示例代码:依赖链分析
[...]奔腾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个周期。我不明白为什么依赖链不是比那更大的瓶颈。
我看到作者解释说它与无序执行和流水线连接,但我看不到它。不过,我的意思是,只有乘法会导致延迟5个周期,所以只有这个值大于4个周期。
我也无法理解为什么笔者不关心的依赖性在这里:
add eax, 16 -> cmp eax, ecx -> jl L1
毕竟,之前cmp
和cmp
必须jl
之前执行除了必须执行。
PS:后面的段落标识最大的瓶颈为奔腾M作为解码,将其限制到每6C一次迭代,因为128B矢量OPS进行解码,以每两个微指令。请参阅Agner Fog的指南以了解其他分析,以及Core2,FMA4推土机和Sandybridge的分析+调整。
比较/分支对将被预测,因此它不会真正计数。除此之外,我不知道你在问什么 – harold
你能否把Agner的文档链接起来,并陈述你所引用的部分和范例? –