使用xor reg,reg是否优于mov reg,0?

sha*_*oth 48 x86 assembly micro-optimization

在x86上有两种众所周知的方法可以将整数寄存器设置为零值.

mov reg, 0
Run Code Online (Sandbox Code Playgroud)

要么

xor reg, reg
Run Code Online (Sandbox Code Playgroud)

有一种观点认为第二种变体更好,因为值0没有存储在代码中并且节省了几个字节的生成的机器代码.这绝对是好的 - 使用较少的指令缓存,这有时可以实现更快的代码执行.许多编译器生成这样的代码.

然而,在xor指令和改变相同寄存器的早期指令之间正式存在指令间依赖性.由于存在依赖性,后一条指令需要等到前者完成,这可能会减少处理器单元的负载并损害性能.

add reg, 17
;do something else with reg here
xor reg, reg
Run Code Online (Sandbox Code Playgroud)

很明显,无论初始寄存器值如何,xor的结果都将完全相同.但是处理器能够识别出这个吗?

我在VC++ 7中尝试了以下测试:

const int Count = 10 * 1000 * 1000 * 1000;
int _tmain(int argc, _TCHAR* argv[])
{
    int i;
    DWORD start = GetTickCount();
    for( i = 0; i < Count ; i++ ) {
        __asm {
            mov eax, 10
            xor eax, eax
        };
    }
    DWORD diff = GetTickCount() - start;
    start = GetTickCount();
    for( i = 0; i < Count ; i++ ) {
        __asm {
            mov eax, 10
            mov eax, 0
        };
    }
    diff = GetTickCount() - start;
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

通过优化两个循环完全相同的时间.这是否合理地证明处理器认识到xor reg, reg指令对先前mov eax, 0指令没有依赖性?什么可以是一个更好的测试来检查这个?

Mar*_*ark 29

一个真正的答案:

英特尔64和IA-32架构优化参考手册

第3.5.1.8节是你想看的地方.

简而言之,存在xor或mov可能是优选的情况.问题围绕依赖链和条件代码的保存.


ajs*_*410 13

x86具有可变长度指令.MOV EAX,0在代码空间中需要比XOR EAX,EAX多一个或两个字节.

  • `mov eax,0`是5个字节:一个用于`mov eax,imm32`操作码,4用于立即数据的4B.`xor eax,eax`是2个字节:一个`xor r32,r/m32`操作码,一个用于操作数. (8认同)

pax*_*blo 12

我卖掉1966年的人力资源旅行车后,我停止了修理自己的车.我对现代CPU有类似的修复:-)

它实际上将取决于底层的微码或电路.很可能CPU可以识别"XOR Rn,Rn"并简单地将所有位清零而不用担心内容.但是,当然,它可能会做同样的事情"MOV Rn, 0".一个好的编译器会为目标平台选择最好的变体,所以如果你在汇编程序中编码,这通常只是一个问题.

如果CPU足够智能,您的XOR依赖关系就会消失,因为它知道该值是无关紧要的,并且无论如何都会将其设置为零(这又取决于所使用的实际CPU).

但是,我很久以前在我的代码中关注了几个字节或几个时钟周期 - 这似乎是微优化变得疯狂.

  • 无论是否为实际使用进行过度优化,理解并非所有类似指令都是相同的,这可能是有价值的.;) (4认同)
  • @jerryjvl - 了解现代桌面x86 CPU不运行x86机器代码也很有用 - 它们将x86解码为RISC,就像要执行的内部指令一样.因此,他们可以识别公共代码序列(如xor eax,eax)并将它们转换为更简单的指令,例如可能是一些"clear reg"指令.在这种情况下,可能没有实际的xor. (3认同)

Bru*_*son 12

在现代CPU上,首选XOR模式.它更小,更快.

较小实际上确实很重要,因为在许多实际工作负载上,限制性能的主要因素之一是i-cache未命中.这不会在比较这两个选项的微基准测试中捕获,但在现实世界中,它将使代码运行得更快.

并且,忽略减少的i-cache未命中,过去多年中任何CPU上的XOR与MOV相同或更快.什么比执行MOV指令更快?根本不执行任何指令!在最近的英特尔处理器上,调度/重命名逻辑识别XOR模式,"实现"结果将为零,并且仅将寄存器指向物理零寄存器.然后抛弃指令,因为不需要执行它.

最终结果是XOR模式使用零执行资源,并且在最近的Intel CPU上,每个周期可以"执行"四个指令.每个周期MOV最高可达三个指令.

有关详细信息,请参阅我写的这篇博文:

https://randomascii.wordpress.com/2012/12/29/the-surprising-subtleties-of-zeroing-a-register/

大多数程序员不应该担心这一点,但编译器编写者必须担心,并且理解正在生成的代码是好的,而且它很酷!