某些通用寄存器是否比其他寄存器更快?

11 performance x86 assembly x86-64 cpu-registers

在 x86-64 中,如果某些通用寄存器比其他寄存器更受欢迎,某些指令会执行得更快吗?

例如,mov eax, ecx执行速度会比mov r8d, ecx? 我可以想象后者需要一个 REX 前缀,这会使指令获取速度变慢?

使用rax而不是怎么样rcx?怎么样add还是xor?其他操作?较小的寄存器,如r15bvs al? alah?

AMD VS 英特尔?较新的处理器?旧处理器?指令组合?

澄清:某些通用寄存器是否应该优先于其他寄存器,它们是哪些?

nic*_*pro 6

以 EBP、RBP 或 R13 为基础的 LEA 会变慢(PDF 警告,第 3-22 页)。但通常答案是否定的。

退后一步,重要的是要认识到,自从寄存器重命名出现以来,架构寄存器并不处理大多数微架构上的实际物理寄存器。例如,每个 Cascade Lake 内核都有一个由 180 个整数和 168 个 FP 寄存器组成的寄存器文件。

  • 如果寻址模式还没有“+displacement”常量,它只会使 LEA 在 Intel 上变慢。例如 `lea eax, [rbp + 12]` 是可编码的,并且与 `lea eax, [rcx + 12]` 一样快。你所说的效果是我认为 `lea eax, [rbp + rcx*4]` 只能在机器代码中编码为 `lea eax, [rbp + rcx*4 + 0]` (因为 [addressing mode escape-code stuff](/sf/answers/3676592111/)),这是一个 3 组件 LEA,因此在 Intel 上速度很慢。在 AMD 上,拥有缩放索引已经使其成为慢速 LEA。 (2认同)

Pet*_*des 6

一般来说,架构寄存器都是相同的,并重命名为大量物理寄存器。

(除了部分寄存器可能会更慢,尤其是高字节 AH/BH/CH/DH ,在 Haswell 和更高版本上写入完整寄存器后读取速度很慢。请参阅Haswell/Skylake 上的部分寄存器究竟如何执行?编写 AL 似乎对 RAX 有错误的依赖,AH 是不一致的,还有为什么 GCC 不使用部分寄存器?对于写入 8 位和 16 位寄存器时的问题)。这个答案的其余部分只是考虑 32/64-bit 操作数大小。)

但是某些指令需要特定的寄存器,例如传统的可变计数移位(没有 BMI2 shrx 等)需要 CL 中的计数。除法要求 EDX:EAX 中的被除数(对于较慢的 64 位版本,或 RDX:RAX)。

使用像 RBX 这样的调用保留寄存器意味着你的函数必须花费额外的指令来保存/恢复它。

但是,如果您需要更多说明,当然会有性能差异。因此,让我们假设所有其他条件都相同,只需通过更改用于其操作数的寄存器来讨论单个指令的 uops、延迟和代码大小。 TL:DR:唯一的性能差异是由于指令编码限制/差异。 有时不同的寄存器将允许/要求(或让汇编程序选择)不同的编码,作为特殊情况,通常会更小/更大,有时甚至以不同的方式执行。

通常,较小的代码速度更快,并且在 uop 缓存和 I-cache 中的打包效果更好,因此除非您分析了特定情况并发现了问题,否则最好使用较小的编码。通常这意味着在 AL 中保留一个字节值,以便您可以使用这些特殊情况指令,并避免使用 RBP / R13 作为指针。


特定编码特别慢的特殊情况,而不仅仅是大小

如果寻址模式还没有+displacement常量,以 RBP 或 R13 为基础的 LEA 在 Intel 上可能会更慢。

eglea eax, [rbp + 12]可按原样编码,并且与lea eax, [rcx + 12].

lea eax, [rbp + rcx*4]只能在机器代码中编码为lea eax, [rbp + rcx*4 + 0](因为寻址模式转义码的东西),这是一个 3 组件 LEA,因此在英特尔上较慢(Sandybridge 系列上的 3 个周期延迟而不是 1 个周期,请参阅https:/ /agner.org/optimize/指令表和microarch PDF)。在 AMD 上,即使有一个缩放索引,它也已经是一个慢 LEAlea eax, [rdx + rcx*4]

在 LEA 之外,在任何寻址模式中使用 RBP/R13 作为基址总是需要一个disp8/32字节或双字,但我认为实际 AGU 对于 3 分量寻址模式并不慢。所以这只是代码大小的影响。


其他案例包括哪个英特尔微架构引入了 ADC reg,0 single-uop 特殊案例?adc al, imm8即使在像 Skylake 这样的现代 uarches 上,其中的短格式 2 字节编码也是 2 uop,其中adc bl, imm81 uop。

因此,这种adc reg,0特殊情况不仅不适用于adc al,0通过 Haswell 在 Sandybridge 上工作,Broadwell 和更新的忘记(或选择不)优化编码解码为 uops 的方式。(当然,您可以adc al,0使用 3 字节的 Mod/RM 编码手动编码,但汇编器将始终选择最短的编码,因此adc al,0默认情况下会汇编为短格式。)只有字节寄存器有问题;adc eax,0将使用opcode ModRM imm83-byte 编码,而不是 5-byte opcode imm32

对于 的其他情况op al,imm8,唯一的区别是代码大小,这仅对性能有间接影响。 (因为解码、uop-cache 打包和 I-cache 未命中)。

请参阅在 x86/x64 机器代码中打高尔夫球的技巧,了解有关代码大小特殊情况的更多信息,例如xchg eax, ecx1 字节与xchg edx, ecx2 字节。


add rsp, 8如果自上次 push/pop/call/ret 以来没有明确使用 RSP 或 ESP(当然沿着执行路径,而不是在静态代码布局中),则可能需要额外的堆栈同步 uop。(Sandybridge 微架构中的堆栈引擎是什么?)。这就是为什么编译器喜欢clang使用虚拟推入或弹出来保留/释放单个堆栈槽:为什么这个函数将 RAX 作为第一个操作推入堆栈?


归档时间:

查看次数:

307 次

最近记录:

5 年 前