按位运算符,而不是xor用于分支
在提出这个问题之后,我收到了@ AndonM.Coleman的一个非常有趣的评论,我必须证实。
由于您的反汇编代码是为x86编写的,值得指出的是XOR将设置/清除零标记,而NOT不会(如果要在不影响依赖于先前操作标记的跳转条件的情况下执行按位操作,则有时不会有用) 。 现在,考虑到你不是直接编写程序集,你真的没有办法以有意义的方式访问这个标志,所以我怀疑这是支持这个标志的原因。
他的评论让我很好奇,如果下面的代码会产生相同的汇编指令
#include <iostream>
int main()
{
unsigned int val = 0;
std::cout << "Enter a numeric value: ";
std::cin >> val;
if ( (val ^ ~0U) == 0)
{
std::cout << "Value inverted is zero" << std::endl;
} else
{
std::cout << "Value inverted is not zero" << std::endl;
}
if ( (~val) == 0)
{
std::cout << "Value inverted is zero" << std::endl;
} else
{
std::cout << "Value inverted is not zero" << std::endl;
}
return 0;
}
对于以下两个操作
if ( (val ^ ~0U) == 0 )
和
if ( (~val) == 0 )
Visual Studio 2010中的未优化版本提供以下反汇编:
if ( (val ^ ~0U) == 0)
00AD1501 mov eax,dword ptr [val]
00AD1504 xor eax,0FFFFFFFFh
00AD1507 jne main+86h (0AD1536h)
if ( (~val) == 0)
00AD1561 mov eax,dword ptr [val]
00AD1564 not eax
00AD1566 test eax,eax
00AD1568 jne main+0E7h (0AD1597h)
我的问题是关于优化。 写作会更好吗?
if ( (val ^ ~0U) == 0)
要么
if ( (~val) == 0)
这取决于很多事情,但主要是你告诉编译器优化的是什么(如果有的话)。
如果编译器被设置为优化大小(最小字节码),那么有时它会在看起来很奇怪的地方使用XOR
。 例如,X86使用的可变长度编码方案可以通过以比使用MOV
指令所需的更少的代码字节XOR
的方式将寄存器设置为0 。
考虑使用XOR
的代码:
if ( (val ^ ~0U) == 0 ) /* 3-bytes to negate and test (x86) */
XOR eax,0FFFFFFFFh
需要3个字节的AND置位 /清零零标记(ZF)
现在,考虑使用NOT
的代码:
if ( (~val) == 0) /* 4-bytes to negate and test (x86) */
NOT eax
编码为2字节指令,但不影响CPU标志。
TEST eax,eax
增加了2个字节,并且需要设置/清除零标志(ZF)
NOT
也是一个简单的指令,但由于它不会影响任何CPU标志,因此必须随后发出TEST
指令才能将其用于分支,如代码中所示。 这实际上会产生更大的字节码,所以一个智能编译器设置为优化大小可能会尝试避免使用NOT
。 这两种指令一起完成需要多少次循环才能完成CPU生成,而智能编译器在被告知优化速度时也会将其纳入其决策制定过程中。
如果您没有编写手动调试的程序集,最好使用最清晰的任何人,并希望编译器足够聪明,可以选择不同的指令/调度等。 在编译时按照要求优化大小/速度。 编译器具有一套聪明的启发式选择,用于选择和调度指令,他们比目标CPU架构更了解目标CPU体系结构。
如果你后来发现这个分支真的是一个瓶颈,并且没有更高级的解决方法,那么你可以做一些低级调优。 然而,除非您的目标是低功耗嵌入式CPU或内存有限的设备,否则这些重点都集中在这些小事上。 我唯一能够通过手动调整获得足够性能的地方是,它们的算法受益于数据并行性以及编译器不够聪明,无法有效利用MMX / SSE等专用指令集。
链接地址: http://www.djcxy.com/p/58243.html