【发布时间】:2021-02-23 18:04:51
【问题描述】:
在最新的 Intel CPU 上,POP 指令的吞吐量通常为每个周期 2 条指令。但是,当使用寄存器R12(或RSP,除了前缀具有相同的编码)时,如果指令通过传统解码器,吞吐量将下降到每个周期1(如果吞吐量保持在每个周期大约2,如果µops 来自 DSB)。
这可以使用nanoBench 复制如下:
sudo ./nanoBench.sh -asm "pop R12"
在 Haswell 机器上的进一步实验表明:当在 1 和 4 之间添加 nops,
sudo ./nanoBench.sh -asm "pop R12; nop;"
sudo ./nanoBench.sh -asm "pop R12; nop; nop;"
sudo ./nanoBench.sh -asm "pop R12; nop; nop; nop;"
sudo ./nanoBench.sh -asm "pop R12; nop; nop; nop; nop;"
执行时间增加到 2 个周期。添加第 5 个nop 时,
sudo ./nanoBench.sh -asm "pop R12; nop; nop; nop; nop; nop;"
执行时间增加到 3 个周期。这表明没有其他指令可以在与pop R12 指令相同的周期内解码。 (当使用不同的寄存器时,例如R11,最后一个示例需要 1.5 个周期。)
在 Skylake 上,在 1 和 3 nops 之间相加时,执行时间保持在 1 个周期,而在 4 和 7 nops 之间增加至 2。这表明 pop R12 是一条需要复杂解码器的指令,即使它只有一个 µop(另见 Can the simple decoders in recent Intel microarchitectures handle all 1-µop instructions?)
为什么在使用寄存器R12 时,POP 指令的解码方式不同?是否还有其他说明也是如此?
【问题讨论】:
-
r12与rsp具有相同的编码,除了REX前缀中的位,也许这会“吓到”解码器? -
@harold:这是有道理的;
pop reg的缩写形式将寄存器编码为“操作码”字节的一部分,并且可能是哪个解码器的主要过滤可以通过操作码字节解码哪个指令(不考虑前缀)。我想知道我们是否会看到与pop r/m64的 2 字节 ModRM 编码相同的效果,它通过 ModRM 字节对 RSP 或 R12 进行编码。pop rsp很特别,因为它只是mov rsp, [old_rsp](或者正如手册所说,在应用增量之后编写 R/ESP,但仍从旧的栈顶加载。felixcloutier.com/x86/pop )。 -
@PeterCordes 使用
pop R12的2字节ModRM编码,效果不会出现。 -
也许值得为 GAS 和 NASM 提交补丁,以便使用此指令优化速度而不是大小。或者让 GCC 尽可能避免使用 R12,以避免在结尾处弹出 r12。或者在 ret 之前将其排成 4 个 insn,这样您就可以在多指令
ret到达解码器之前获得完整的解码组。 -
@PeterCordes 在 Haswell 上,每 ~25 个
pop R12就有一个堆栈同步微指令;没有额外的堆栈同步微指令。对于pop RSP,没有堆栈同步微指令;但是,pop RSP解码为 3 uop,而pop R12仅解码为 1 uop(对于两种编码)。
标签: performance x86 intel cpu-architecture micro-optimization