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 指南(如果您还没有的话)。他们充满了基本知识。