x86_64 - 程序集 - 循环条件和乱序
x86_64 - Assembly - loop conditions and out of order
我不是要求基准。
(如果是那样,我早就自己做了)
我的问题:
为了方便起见,我倾向于避免使用 indirect/index 寻址模式。
作为替代,我经常使用立即、绝对或寄存器寻址。
代码:
; %esi has the array address. Say we iterate a doubleword (4bytes) array.
; %ecx is the array elements count
(0x98767) myloop:
... ;do whatever with %esi
add , %esi
dec %ecx
jnz 0x98767;
在这里,我们有一个序列化的组合(dec 和 jnz),它可以防止正确的乱序执行(依赖)。
有没有办法避免这种情况/破坏部门? (我不是汇编高手)
针对 Intel CPU 进行优化时,始终将标志设置指令放在条件跳转指令之前(如果它是下面 table 中列出的简单指令之一),这样它们就可以宏融合到解码器中的一个 uop。
对于不进行宏融合的旧 CPU,这样做并没有明显的恶化。对于此类 CPU,将标志设置提早放置可能会将分支预测错误的惩罚缩短一个,但乱序执行意味着将 dec
提早移动几条指令不会产生真正的影响。另见 。为了真正有所作为,你做一些事情,比如在可以更简单地计算的东西上展开循环 and/or 分支,理想情况下不依赖于慢速输入,所以 OoO exec 可以在处理旧的时解决分支循环体的迭代。即循环计数器 dep-chain 可以 运行 领先于主要工作。
我没有基准测试,但我认为越来越少见的 CPU 的小缺点不能证明错过融合 CPU 的前端吞吐量优势(解码和发布)是合理的。 uop 总吞吐量通常是瓶颈。
AMD Bulldozer/Piledriver/Steamroller 可以将 test/cmp
与任何 jcc
融合,但只能 test/cmp
,不能融合任何其他 ALU 指令。所以绝对要与分支机构进行比较。如果英特尔 CPU 可以在 sandybridge-family 上进行宏融合,那么将其他东西与分支放在一起仍然很有价值。
来自 Agner Fog's 微架构指南,Table 9.2(对于 Sandybridge / Ivybridge):
First | can pair with these | cannot pair with
instruction | (and the inverse) |
---------------------------------------------
cmp |jz, jc, jb, ja, jl, jg| js, jp, jo
add, sub |jz, jc, jb, ja, jl, jg| js, jp, jo
adc, sbb |none |
inc, dec |jz, jl, jg | jc, jb, ja, js, jp, jo
test | all |
and | all |
or, xor, not, neg | none |
shift, rotate | none |
Table 9.2. Instruction fusion
所以基本上,inc/dec
可以与 jcc
宏融合,只要条件仅取决于由 inc/dec
修改的位。
(否则,它们不会进行宏融合,并且您会插入一个额外的 uop 来合并标志(就像您在写入 al
之后读取 eax
时)。或者在更早的 CPU 上, 部分标志停顿。)
Core2 / Nehalem 的宏融合能力更受限制(仅针对 CMP/TEST 具有更有限的 JCC 组合),而 Core2 根本无法在 64 位模式下进行宏融合。
也请阅读 Agner Fog 的优化 asm 和 C 指南(如果您还没有的话)。他们充满了基本知识。
我不是要求基准。
(如果是那样,我早就自己做了)
我的问题:
为了方便起见,我倾向于避免使用 indirect/index 寻址模式。
作为替代,我经常使用立即、绝对或寄存器寻址。
代码:
; %esi has the array address. Say we iterate a doubleword (4bytes) array.
; %ecx is the array elements count
(0x98767) myloop:
... ;do whatever with %esi
add , %esi
dec %ecx
jnz 0x98767;
在这里,我们有一个序列化的组合(dec 和 jnz),它可以防止正确的乱序执行(依赖)。
有没有办法避免这种情况/破坏部门? (我不是汇编高手)
针对 Intel CPU 进行优化时,始终将标志设置指令放在条件跳转指令之前(如果它是下面 table 中列出的简单指令之一),这样它们就可以宏融合到解码器中的一个 uop。
对于不进行宏融合的旧 CPU,这样做并没有明显的恶化。对于此类 CPU,将标志设置提早放置可能会将分支预测错误的惩罚缩短一个,但乱序执行意味着将 dec
提早移动几条指令不会产生真正的影响。另见
我没有基准测试,但我认为越来越少见的 CPU 的小缺点不能证明错过融合 CPU 的前端吞吐量优势(解码和发布)是合理的。 uop 总吞吐量通常是瓶颈。
AMD Bulldozer/Piledriver/Steamroller 可以将 test/cmp
与任何 jcc
融合,但只能 test/cmp
,不能融合任何其他 ALU 指令。所以绝对要与分支机构进行比较。如果英特尔 CPU 可以在 sandybridge-family 上进行宏融合,那么将其他东西与分支放在一起仍然很有价值。
来自 Agner Fog's 微架构指南,Table 9.2(对于 Sandybridge / Ivybridge):
First | can pair with these | cannot pair with
instruction | (and the inverse) |
---------------------------------------------
cmp |jz, jc, jb, ja, jl, jg| js, jp, jo
add, sub |jz, jc, jb, ja, jl, jg| js, jp, jo
adc, sbb |none |
inc, dec |jz, jl, jg | jc, jb, ja, js, jp, jo
test | all |
and | all |
or, xor, not, neg | none |
shift, rotate | none |
Table 9.2. Instruction fusion
所以基本上,inc/dec
可以与 jcc
宏融合,只要条件仅取决于由 inc/dec
修改的位。
(否则,它们不会进行宏融合,并且您会插入一个额外的 uop 来合并标志(就像您在写入 al
之后读取 eax
时)。或者在更早的 CPU 上, 部分标志停顿。)
Core2 / Nehalem 的宏融合能力更受限制(仅针对 CMP/TEST 具有更有限的 JCC 组合),而 Core2 根本无法在 64 位模式下进行宏融合。
也请阅读 Agner Fog 的优化 asm 和 C 指南(如果您还没有的话)。他们充满了基本知识。