【发布时间】: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 个周期。我不明白为什么依赖链不是比这更大的瓶颈。
我看到作者解释说它与乱序执行和流水线有关,但我看不到。不过,我的意思是,只有乘法会导致延迟 5 个周期,所以只有这个值大于 4 个周期。
我也无法理解为什么作者不关心这里的依赖:
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