【问题标题】:x86_64 - Assembly - loop conditions and out of orderx86_64 - 汇编 - 循环条件和乱序
【发布时间】:2015-10-24 15:06:30
【问题描述】:

要求一个基准。

(如果是这样的话,我会自己做的。)


我的问题:

为了方便,我倾向于避免使用间接/索引寻址模式。

作为替代,我经常使用立即寻址、绝对寻址或寄存器寻址。

代码:

; %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 $4, %esi
    dec %ecx
    jnz 0x98767;

在这里,我们有一个序列化的组合(dec 和 jnz),它可以防止正确的乱序执行(依赖)。

有没有办法避免这种情况/破坏dep? (我不是装配专家)。

【问题讨论】:

  • 所以让我直截了当地说:您想要一个取决于前一条指令的结果的条件跳转,可以与该指令乱序执行?我认为这在逻辑上是不可能的。
  • 另请注意 dec 不推荐,因为它会导致部分标志更新停滞。
  • @Jester:那我应该用一个子吗?
  • 您可以使用lea 4(%esi),%esi 进行添加,这不会影响标志,因此您可以在更高的位置插入subl $1, %ecx。正如@davmac 所说,除非您使用再次不推荐的loop 指令,否则您无法摆脱依赖关系。
  • 如果可能,还要确保展开循环,以摊销循环开销的成本。

标签: loops assembly conditional-statements x86-64


【解决方案1】:

在针对 Intel CPU 进行优化时,始终将标志设置指令放在条件跳转指令之前(如果它是下表中列出的简单指令之一),以便它们可以宏融合到解码器中的一个 uop 中。

对于不进行宏融合的旧 CPU 来说,这样做并没有明显更糟。更早地设置标志可能会将此类 CPU 的分支错误预测惩罚缩短一个,但无序执行意味着将 dec 提前移动几条指令不会产生真正的影响。另见Avoid stalling pipeline by calculating conditional early。为了真正有所作为,您可以在可以更简单地计算的东西上展开循环和/或分支之类的事情,理想情况下不依赖于慢速输入,因此 OoO exec 可以在处理较旧的迭代时已经解决分支循环体。即循环计数器 dep-chain 可以在主要工作之前运行。

我没有基准测试,但我认为越来越稀有的 CPU 的小缺点不能证明错过了融合 CPU 的前端吞吐量优势(解码和问题)。 uop 总吞吐量通常会成为瓶颈。

AMD Bulldozer/Piledriver/Steamroller 可以将test/cmp 与任何jcc 融合,但只能融合test/cmp,不能融合任何其他ALU 指令。所以肯定把它与分支进行比较。如果 Intel CPU 可以在 sandybridge-family 上进行宏融合,那么将其他东西放在分支上仍然很有价值。

来自Agner Fog's microarch 指南,表 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 的宏融合能力更有限(仅适用于 JCC 组合更有限的 CMP/TEST),Core2 根本无法在 64 位模式下进行宏融合。

如果您还没有阅读 Agner Fog 的优化 asm 和 C 指南,也可以阅读。它们充满了基本知识。

【讨论】:

  • 非常感谢彼得,我已经从他那里得到了“指令表”和“优化装配”。虽然我没有完全阅读后者(因此我的无知),但我现在会这样做。谢谢彼得:)
  • @Kroma:此表来自 microarchitecture.pdf。我忘记了他是否在优化 asm 指南中提到了宏融合,但可能至少提到了。
  • 可能值得一提的是,第一条指令和第二条指令必须在同一个 16 字节解码段中才能工作(将相同的地址向下舍入到 16 字节)。想想 Agner Fog 在某处提到过这一点。
  • @Noah:首先,解码组并不总是对齐的。其次,如果 Sandybridge-family 是一个融合候选者,它会保留组中的最后一条指令,以防下一组中的第一条指令是分支。 (因此它牺牲了一些传统解码吞吐量,以可能构建更紧凑的 uop-cache 行,并可能最大限度地减少 ROB 空间和其他后端资源消耗)。我认为这已经在某处讨论过,但没有想到任何具体的内容。不过,您可能会在 google 上找到一些东西。
猜你喜欢
  • 2013-07-25
  • 2022-10-18
  • 2012-08-12
  • 1970-01-01
  • 2014-01-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-11-30
相关资源
最近更新 更多