位运算符更快吗?如果是,那为啥?

Posted

技术标签:

【中文标题】位运算符更快吗?如果是,那为啥?【英文标题】:Are bitwise operators faster?, if yes then why?位运算符更快吗?如果是,那为什么? 【发布时间】:2020-07-26 05:04:40 【问题描述】:

如果我使用它会对性能有多大影响:

n>>1 instead of n/2
n&1 instead of n%2!=0
n<<3 instead of n*8
n++ instead of n+=1
and so on...

如果确实提高了性能,请解释原因。

【问题讨论】:

一个好的优化编译器会识别这些代码 sn-ps 并生成相同的代码。像您这样的问题可能在几十年前就具有相关性,但在当今时代却没有。 可能没有区别。编译器会优化你写的任何东西,并将其换成它可以想到的最快版本,如果你不优化,为什么蓝精灵你关心速度? 这能回答你的问题吗? C++ style vs. performance? 你可以编写一个小程序来执行这些数百万次并为它们计时 在不知道n 是什么类型的情况下无法回答这个问题。对于无符号整数类型,编译器将一种转换为另一种是微不足道的。对于有符号整数类型或运算符重载的类型,操作可能不等价。 【参考方案1】:

任何半体面的编译器都会将这两个版本优化成同一个东西。例如,GCC 编译这个:

unsigned int half1(unsigned int n)  return n / 2; 
unsigned int half2(unsigned int n)  return n >> 1; 
bool parity1(int n)  return n % 2; 
bool parity2(int n)  return n & 1; 
int mult1(int n)  return n * 8; 
int mult2(int n)  return n << 3; 
void inc1(int& n)  n += 1; 
void inc2(int& n)  n++; 

half1(unsigned int):
        mov     eax, edi
        shr     eax
        ret
half2(unsigned int):
        mov     eax, edi
        shr     eax
        ret
parity1(int):
        mov     eax, edi
        and     eax, 1
        ret
parity2(int):
        mov     eax, edi
        and     eax, 1
        ret
mult1(int):
        lea     eax, [0+rdi*8]
        ret
mult2(int):
        lea     eax, [0+rdi*8]
        ret
inc1(int&):
        add     DWORD PTR [rdi], 1
        ret
inc2(int&):
        add     DWORD PTR [rdi], 1
        ret

需要注意的是,在第一个示例中,如果 n 可能为负数(以防它已签名且编译器无法证明它是非负数),则除法和位移不等价,并且除法需要一些额外的指令。除此之外,编译器很聪明,它们会使用常量操作数优化操作,所以使用更符合逻辑且更具可读性的版本。

【讨论】:

【参考方案2】:

严格来说,在大多数情况下,是的。

这是因为位操作对于 CPU 来说是一种更简单的操作,因为 APU 中的电路要简单得多,并且需要更少的离散步骤(时钟周期)来完全执行。

正如其他人所提到的,任何值得一提的编译器都会自动检测具有按位模拟的某些算术运算的常量操作数(如您的示例中的那些),并将它们转换为引擎盖下的适当按位运算。

请记住,如果操作数是运行时值,则无法进行此类优化。

【讨论】:

以上是关于位运算符更快吗?如果是,那为啥?的主要内容,如果未能解决你的问题,请参考以下文章

位运算符的问题,&不能进行int,float运算。为啥?

x86 OS 的 64 位处理器上的 64 位整数运算

verilog 为啥乘法器写的那么复杂? 不是这样写也可以吗 assign c = a * b;

C语言 位运算

位运算

来自小姐姐的灵魂拷问:位运算是什么?