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

Are some general purpose registers faster than others?

在 x86-64 中,如果某些通用寄存器优于其他寄存器,某些指令的执行速度是否会更快?

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

rax 代替 rcx 怎么样? addxor 呢?其他操作?像 r15bal 这样的小寄存器? alah?

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

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

LEA will be slower with EBP, RBP, or R13 as the base(PDF 警告,第 3-22 页)。但一般答案是否定的

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

总的来说,架构寄存器都是平等的,重命名到一大堆物理寄存器上。

(除了部分寄存器可能更慢,尤其是高字节 AH/BH/CH/DH 在写入完整寄存器后 读取 很慢,在 Haswell 和更高版本上。参见 and also 写 8 位和 16 位寄存器时的问题)。 这个答案的其余部分将只考虑 32/64 位操作数大小。)

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

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

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

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


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

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

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

一样快

但是 lea eax, [rbp + rcx*4] 只能在机器代码中编码为 lea eax, [rbp + rcx*4 + 0](因为 ), which is a 3-component LEA, and thus slower on Intel (3 cycle latency on Sandybridge-family instead of 1 cycle, see https://agner.org/optimize/ 指令表和微架构 PDF)。在 AMD 上,即使使用 lea eax, [rdx + rcx*4]

,具有缩放索引也已经使它成为慢速 LEA

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


其他情况包括 ,其中 adc al, imm8 的短格式 2 字节编码即使在像 Skylake 这样的现代 uarches 上也是 2 微指令,其中 adc bl, imm8 是 1 微指令。

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

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

有关代码大小特殊情况的更多信息,请参阅 Tips for golfing in x86/x64 machine code,例如 xchg eax, ecx 是 1 字节与 xchg edx, ecx 是 2 字节。


add rsp, 8 如果自上次 push/pop/call/ret 以来没有明确使用 RSP 或 ESP(当然,沿着执行路径,而不是在静态代码布局)。 (). This is why compilers like clang use a dummy push or pop to reserve / free a single stack slot:

你一共塞了太多的问题,但是,如果我很好地理解了这个问题,你是在混淆处理器架构和小而快的寄存器文件,它填补了处理器和内存技术之间的速度差距。寄存器文件足够小,一次只能支持一条指令,即当前指令,而且速度足够快,几乎可以赶上处理器速度。

我想简单介绍一下,这些寄存器的命名约定有两个目的:一,它使旧版本的x86 ISA实现兼容到现在,二,这些寄存器的每个名称都有除了它的一般用途之外,它还有一个特殊用途。例如,ECX 寄存器用作计数器来实现循环,即像 JECXZ 和 LOOP 这样的指令专门使用 ECX 寄存器。尽管您需要注意一些您不想丢失的标志。

现在你的问题的答案源于第二个目的。所以有些寄存器似乎更快,因为这些特殊寄存器被硬编码到处理器中并且可以更快地访问,但是,差异应该不会太大。

第二件事你可能知道,并不是所有的指令都具有相同的复杂性,尤其是在 x86 中,指令的操作码可以是 1-3 个字节,并且随着越来越多的功能被添加到指令中在前缀、寻址模式等方面,这些指令开始变慢,所以并不是某些寄存器比其他寄存器慢,只是一些寄存器被编码到指令中,因此那些指令 运行 使用该寄存器组合更快。如果以其他方式使用,它会显得更慢。我希望这有帮助。谢谢