不,融合与一条复杂指令(如cpuid 或lock 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/sub 或 inc/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:u 和 uops_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:u 比 uops_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?.