以 RAX/EAX/AX/AL/AH 寄存器作为目的地的加法是否更快?
Is an addition with carry faster with RAX/EAX/AX/AL/AH registers as destination?
在英特尔文档中,我们有 ADC
的下一个定义:
Op/En Operand 1 Operand 2 .....
RM ModRM:reg (r, w) ModRM:r/m (r)
MR ModRM:r/m (r, w) ModRM:reg (r)
MI ModRM:r/m (r, w) imm8
I AL/AX/EAX/RAX imm8
现在 asm
代码的一个小例子:
asm (
"adc -Ox12(%rbp), %rax \n\t" //1
"adc -Ox12(%rbp), %rdx \n\t" //2
"adc -Ox12(%rbp), %r8 \n\t" //3
"adc -Ox12(%rbp), %R11 \n\t" //4
"adc %r8 , %rdx \n\t" //5
"adc %r8 , %rax \n\t" //6
"adc , %rdx \n\t" //7
"adc , %rax \n\t" //8
);
你能告诉我每组中哪条指令最快吗?为什么?
我有这个问题,因为在 Intel 中他们引用 %RAX
寄存器。另一个慢吗?
注意:对于下面的所有内容,我假设是现代 80x86(过去 10 年左右的任何内容)。
对于第一组;第一条指令导致高速缓存未命中或依赖性停顿的可能性(非常小)增加(由 RBP、RAX 或进位标志被引导到它的指令修改引起)。
对于所有其他指令,都依赖于 eflags(它们必须等到知道前一条指令的进位标志),它们都会受到同样的影响。更具体地说,我希望 "carry flag dependency" 将每条指令的执行限制为 1 个周期(没有并行发生的指令)。那是最有可能的瓶颈。
使用的寄存器没有区别(除了依赖于以前使用的寄存器)。
甚至 adc , %rax
也不能有效地使用特殊的 rax-only 编码
REX.W + 15 id
ADC RAX, imm32
.
REX.W
+ 15 03 00 00 00
是 6 个字节。 (adc rax, imm32
)
REX.W
+ 83 mod/rm 03
是 4 个字节。 (adc r/m32, imm8
,其中 mod/rm 字节将 rax
编码为目标,reg
字段中的 /2
是操作码的一部分。立即源操作共享第一个操作码字节。)
The (16bit version of) both encodings were introduced with 8086. See the link in the x86 维基百科。显然,累加器应该一直用于所有事情,and/or 他们没有考虑未来的指令集扩展,所以他们认为在特殊的 al
和 [= 上花费那么多操作码是值得的23=] 所有 ALU 立即数指令的版本。
如果您查看双操作数整数 ALU 指令(and
、or
、sub
、test
等),每一个都有一个特殊的- 用于 al
和 ax
/eax
/rax
目的地的更短字节编码,具有全尺寸立即操作数。 (即 imm32
,而不是 imm8
符号扩展为 32 或 64b)。所以每条指令有两个额外的操作码。
这只影响 x86 代码大小。一旦指令被解码,它们 运行 的方式就没有进一步的区别了。请参阅 http://agner.org/optimize/ 以了解有关 CPU 内部结构的更多信息。
AMD64 可以将这些排除在 64 位模式之外,释放更多编码 space,但他们可能对取消 32 位并不乐观。如果您希望指令在 32 位和 64 位模式下工作,如果两种模式的编码相同,则需要较少的解码器晶体管。不过,他们本可以将编码 space 用于 setcc r32
之类的。没有花哨的新 SIMD 功能,只是取消了一些基本指令。在标志设置操作之前,如果没有 xor
,您几乎永远不能使用 setcc
将整个寄存器归零。无论如何,AMD 错过了从 x86 中删除一些杂物的绝好机会。
有趣的事实:在 Broadwell / Skylake(以及更高版本?)上,具有直接编码 adc
的特殊情况 AL/AX/EAX/RAX 实际上 更慢。参见
这也可能适用于早期 Sandybridge / Haswell 上的 adc al,0
。 (adc eax, 0
不会使用该编码。)
在英特尔文档中,我们有 ADC
的下一个定义:
Op/En Operand 1 Operand 2 .....
RM ModRM:reg (r, w) ModRM:r/m (r)
MR ModRM:r/m (r, w) ModRM:reg (r)
MI ModRM:r/m (r, w) imm8
I AL/AX/EAX/RAX imm8
现在 asm
代码的一个小例子:
asm (
"adc -Ox12(%rbp), %rax \n\t" //1
"adc -Ox12(%rbp), %rdx \n\t" //2
"adc -Ox12(%rbp), %r8 \n\t" //3
"adc -Ox12(%rbp), %R11 \n\t" //4
"adc %r8 , %rdx \n\t" //5
"adc %r8 , %rax \n\t" //6
"adc , %rdx \n\t" //7
"adc , %rax \n\t" //8
);
你能告诉我每组中哪条指令最快吗?为什么?
我有这个问题,因为在 Intel 中他们引用 %RAX
寄存器。另一个慢吗?
注意:对于下面的所有内容,我假设是现代 80x86(过去 10 年左右的任何内容)。
对于第一组;第一条指令导致高速缓存未命中或依赖性停顿的可能性(非常小)增加(由 RBP、RAX 或进位标志被引导到它的指令修改引起)。
对于所有其他指令,都依赖于 eflags(它们必须等到知道前一条指令的进位标志),它们都会受到同样的影响。更具体地说,我希望 "carry flag dependency" 将每条指令的执行限制为 1 个周期(没有并行发生的指令)。那是最有可能的瓶颈。
使用的寄存器没有区别(除了依赖于以前使用的寄存器)。
甚至 adc , %rax
也不能有效地使用特殊的 rax-only 编码
REX.W + 15 id
ADC RAX, imm32
.
REX.W
+15 03 00 00 00
是 6 个字节。 (adc rax, imm32
)REX.W
+83 mod/rm 03
是 4 个字节。 (adc r/m32, imm8
,其中 mod/rm 字节将rax
编码为目标,reg
字段中的/2
是操作码的一部分。立即源操作共享第一个操作码字节。)
The (16bit version of) both encodings were introduced with 8086. See the link in the x86 维基百科。显然,累加器应该一直用于所有事情,and/or 他们没有考虑未来的指令集扩展,所以他们认为在特殊的 al
和 [= 上花费那么多操作码是值得的23=] 所有 ALU 立即数指令的版本。
如果您查看双操作数整数 ALU 指令(and
、or
、sub
、test
等),每一个都有一个特殊的- 用于 al
和 ax
/eax
/rax
目的地的更短字节编码,具有全尺寸立即操作数。 (即 imm32
,而不是 imm8
符号扩展为 32 或 64b)。所以每条指令有两个额外的操作码。
这只影响 x86 代码大小。一旦指令被解码,它们 运行 的方式就没有进一步的区别了。请参阅 http://agner.org/optimize/ 以了解有关 CPU 内部结构的更多信息。
AMD64 可以将这些排除在 64 位模式之外,释放更多编码 space,但他们可能对取消 32 位并不乐观。如果您希望指令在 32 位和 64 位模式下工作,如果两种模式的编码相同,则需要较少的解码器晶体管。不过,他们本可以将编码 space 用于 setcc r32
之类的。没有花哨的新 SIMD 功能,只是取消了一些基本指令。在标志设置操作之前,如果没有 xor
,您几乎永远不能使用 setcc
将整个寄存器归零。无论如何,AMD 错过了从 x86 中删除一些杂物的绝好机会。
有趣的事实:在 Broadwell / Skylake(以及更高版本?)上,具有直接编码 adc
的特殊情况 AL/AX/EAX/RAX 实际上 更慢。参见
这也可能适用于早期 Sandybridge / Haswell 上的 adc al,0
。 (adc eax, 0
不会使用该编码。)