【问题标题】:What is instruction fusion in contemporary x86 processors?当代 x86 处理器中的指令融合是什么?
【发布时间】:2019-10-18 04:35:45
【问题描述】:

我的理解是,指令融合有两种类型:

  1. 微操作融合
  2. 宏操作融合

微操作是可以在 1 个时钟周期内执行的操作。如果几个微操作融合在一起,我们得到一个“指令”。

如果多条指令融合,我们得到一个宏操作。

如果融合了几个宏操作,我们得到宏操作融合。

我说的对吗?

【问题讨论】:

  • 您确实需要熟悉 Agner Fog 的优化手册,尤其是 microarchitecture 的那本。在文档中搜索您感兴趣的 CPU 的“Macro-op fusion”和“Micro-op fusion”。从广义上讲,不同之处在于,在 macro-op fusion 中,两条指令融合在一个 micro-op 中(例如 @987654322 @ & jne 融合成单个递减和条件分支),而微操作融合涉及一起处理多个真正“属于”在一起的微操作,尤其是对于写入和读取-修改-写入指令。跨度>

标签: assembly x86 cpu-architecture


【解决方案1】:

不,融合与一条复杂指令(如cpuidlock add [mem], eax)如何解码为多个微指令完全分开。

退休阶段计算出单个指令的所有微指令都已退休的方式,因此该指令已退休,与融合无关。


宏融合将 cmp/jcc 或 test/jcc 解码为单个比较和分支微指令。(Intel 和 AMD CPU)。管道的其余部分仅将其视为单个 uop1 (性能计数器仍将其视为 2 条指令除外)。这节省了 uop 缓存空间和带宽,包括解码。在某些代码中,compare-and-branch 占总指令组合的很大一部分,可能占 25%,因此选择寻找这种融合而不是其他可能的融合,如 mov dst,src1 / or dst,src2 是有意义的。

Sandybridge 系列还可以在某些条件下将其他一些 ALU 指令与条件分支进行宏融合,例如 add/subinc/dec + JCC。 (x86_64 - Assembly - loop conditions and out of order)


微融合将来自同一条指令的 2 个微指令存储在一起,因此它们只占用管道的融合域部分中的 1 个“槽”。但是他们仍然必须分别分派到不同的执行单元。在英特尔 Sandybridge 系列中,RS(Reservation Station aka 调度程序)位于未融合域中,因此它们甚至单独存储在调度程序中。 (请参阅我在 Understanding the impact of lfence on a loop with two long dependency chains, for increasing lengths 上的回答中的脚注 2。)

P6 家族具有融合域 RS 和 ROB,因此微融合有助于增加无序窗口的有效大小。但据报道,SnB 系列简化了 uop 格式,使其更紧凑,允许使用更大的 RS 尺寸,这一直很有帮助,而不仅仅是微融合指令。

Sandybridge 系列将在某些情况下“取消分层”索引寻址模式,将它们拆分回各自插槽中的 2 个单独的 uop,然后在无序后端中发布/重命名为 ROB,这样您就输了微融合的前端问题/重命名吞吐量优势。见Micro fusion and addressing modes


两者可以同时发生

    cmp   [rdi], eax
    jnz   .target

cmp/jcc 可以宏融合成单个 cmp-and-branch ALU uop,来自[rdi] 的负载可以微融合该 uop。

未能对cmp 进行微熔断不会阻止宏熔断。

这里的限制是:RIP-relative + immediate 永远不能微熔,所以cmp dword [static_data], 1 / jnz 可以宏熔但不能微熔。

SnB 系列上的cmp/jcc(如cmp [rdi+rax], edx / jnz)将在解码器中进行宏熔丝和微熔丝,但微熔丝将在发行阶段之前解除层压。 (所以在融合域和非融合域中总共有 2 个微指令:使用索引寻址模式和 ALU cmp/jnz 加载)。您可以通过在 CMP 和 JCC 与之后放置 mov ecx, 1 来使用性能计数器来验证这一点,并注意 uops_issued.any:uuops_executed.thread 在每次循环迭代时都会上升 1,因为我们击败了宏融合。微融合的表现也一样。

在 Skylake 上,cmp dword [rdi], 0/jnz 无法进行宏熔断。 (仅限微型保险丝)。我用一个包含一些虚拟mov ecx,1 指令的循环进行了测试。重新排序,因此其中一个 mov 指令拆分 cmp/jcc 不会更改融合域或非融合域微指令的性能计数器。

cmp [rdi],eax/jnz 确实宏观和微观融合。重新排序后,mov ecx,1 指令将 CMP 与 JNZ 分开确实会更改性能计数器(证明宏融合),并且 uops_executed 每次迭代都比 uops_issued 高 1(证明微融合)。

cmp [rdi+rax], eax/jne 仅宏熔断器;不是微观的。 (实际上,由于索引寻址模式,解码中的微熔丝但在发布前未分层,并且它不是像sub eax, [rdi+rax] 这样可以保持索引寻址模式微熔丝的RMW 寄存器目标。sub 带有索引寻址模式确实在 SKL 上进行宏融合和微融合,并且可能是 Haswell)。

cmp dword [rdi],0 确实 micro-fuse,但是:uops_issued.any:uuops_executed.thread 小 1,并且循环不包含 nop 或其他“消除”指令,或任何其他可以微融合的内存指令)。

一些编译器(包括 GCC IIRC)更喜欢使用单独的加载指令,然后在寄存器上进行比较+分支。 TODO:检查 gcc 和 clang 的选择是否是立即与寄存器的最佳选择。


微操作是指可以在 1 个时钟周期内执行的操作。

不完全是。它们在管道中占用 1 个“槽”,或者在无序后端跟踪它们的 ROB 和 RS 中。

是的,将微指令分派到执行端口发生在 1 个时钟周期内,简单的微指令(例如整数加法)可以在同一周期内完成执行。自 Haswell 以来,这可能会同时发生多达 8 个微指令,但在 Sunny Cove 上增加到 10 个。实际执行可能需要超过 1 个时钟周期(占用执行单元的时间更长,例如 FP 除法)。

除法器是我认为现代主流 Intel 上唯一未完全流水线化的执行单元,但 Knight's Landing 有一些未完全流水线化的 SIMD shuffle,它们是单个 uop,但(倒数)吞吐量为 2 个周期。)。


脚注 1:

如果cmp [rdi], eax / jne 在内存操作数上出错,即#PF 异常,则将异常返回地址指向cmp 之前。所以我认为即使是异常处理,仍然可以将其视为一个单一的东西。

或者,如果分支目标地址是伪造的,那么在分支已经执行之后,#PF 异常将发生,从带有更新的 RIP 的代码提取中。再说一次,我认为cmp 没有办法成功执行,jcc 出错,需要在 RIP 指向 JCC 时处​​理异常。

但即使这种情况有可能需要设计 CPU 来处理,也可以推迟到实际检测到异常后再进行排序。也许有微码辅助,或者一些特殊情况的硬件。

就 cmp/jcc uop 在正常情况下如何通过管道而言,它的工作方式与设置标志 条件分支的一条长单 uop 指令完全相同。

令人惊讶的是,loop 指令(类似于 dec rcx/jnz,但没有设置标志)不是英特尔 CPU 上的单个微指令。 Why is the loop instruction slow? Couldn't Intel have implemented it efficiently?.

【讨论】:

  • @Hadi:我简化了你的编辑。我认为异常返回地址指向 JCC 时永远不会发生异常。或者如果可以,那是非常特殊的情况,可以特殊处理。无论如何,我做了一些我一直想写的测试,在 SKL cmp dword [rdi], 0 / jnz can't macro-fuse,我猜是因为直接操作数。
  • @ricpacca:不完全是。一个好的心智模型是 ROB 是一个循环缓冲区,由 issue 阶段按顺序写入,在退休阶段按顺序读取。每个条目(单个 uop,可能是微融合的)都有一个标志,指示它是否已完成(准备退休)。我猜还有一个“新指令的开始”标志,或者一个为新指令而改变的 RIP 字段,或者其他什么。确切的机制与性能无关;退休阶段只是在每个周期每个线程中以 4 或 8 个为一组退休 uop,或者无论退休带宽是多少。
  • @BeeOnRope:英特尔称其为 div/sqrt 单位。 arith.divider_active - “当除法单元忙于执行除法或平方根运算时循环”。 sqrtss 的吞吐量瓶颈基本上每个周期都会触发该事件。除法和平方根都是用类似的迭代过程计算的,这就是为什么它们可以有效地共享一个执行单元,以及为什么它们在该 EU 上运行具有非常相似的性能特征。组合 div/sqrt 单位正常:How sqrt() of GCC works after compiled? Which method of root is used? Newton-Raphson?
  • @isma:是的,与微融合不同,宏观融合以后不会重新扩展。这是一个简单的单uop。很确定我在这里的回答已经说明了这一点;如果不告诉我。
  • @Noah:是的,在 RS 和执行单元之前,一切都是融合域。 (融合/非融合域指的是微融合,而不是宏融合;宏融合是一个单独的事情,当指令被路由到冰湖之前的解码器时发生,或者显然是在冰湖中解码之后。虽然 IIRC 有一些如果 cmp/test 具有直接和/或 RIP 相关或其他内容,则融合之间的相互作用。)
猜你喜欢
  • 2019-06-20
  • 1970-01-01
  • 2013-12-06
  • 2016-05-24
  • 2012-10-05
  • 2013-05-20
  • 1970-01-01
相关资源
最近更新 更多