性能方面,Bitwise运算符与正常模数的速度有多快?

问题描述 投票:28回答:7

在正常流程或forif等条件语句中使用按位运算是否会提高整体性能,并且在可能的情况下使用它们会更好吗?例如:

if(i++ & 1) {

}

if(i % 2) {

}
c++ bit-manipulation bitwise-operators
7个回答
36
投票

除非您使用的是古老的编译器,否则它已经可以自己处理这种级别的转换。也就是说,现代编译器可以并且将使用按位i % 2指令实现AND,只要在目标CPU上这样做是有意义的(公平地说,通常会这样做)。

换句话说,不要期望看到它们之间的性能有任何差异,至少对于具有相当合格的优化器的合理的现代编译器而言。在这种情况下,reasonably也有一个相当广泛的定义 - 即使是几十年前的几个编译器也可以毫无困难地处理这种微优化。


26
投票

TL; DR首先写入语义,然后优化测量的热点。

在CPU级别,整数模数和除法是最慢的操作之一。但是您不是在CPU级别编写,而是使用C ++编写,编译器将其转换为中间表示形式,最终根据您要编译的CPU模型将其转换为程序集。

在这个过程中,编译器将应用Peephole Optimizations,其中数字Strength Reduction Optimizations如(courtesy of Wikipedia):

Original Calculation  Replacement Calculation
y = x / 8             y = x >> 3
y = x * 64            y = x << 6
y = x * 2             y = x << 1
y = x * 15            y = (x << 4) - x

最后一个例子可能是最有趣的一个。虽然乘以或除以2的幂可以很容易地(手动)转换为位移操作,但编译器通常被教导执行甚至更智能的转换,您可能会自己想到并且不容易识别(在此处)至少,我个人并不立即认识到(x << 4) - x意味着x * 15)。


8
投票

这显然取决于CPU,但是您可以预期按位操作永远不会花费更多,并且通常需要更少的CPU周期来完成。一般来说,整数/%都很慢,就像CPU指令一样。也就是说,现代CPU管道具有早期完成的特定指令并不意味着您的程序必须运行得更快。

最佳实践是编写可理解,可维护和表达其实现的逻辑的代码。这种微优化非常罕见,因此只有在分析表明存在严重瓶颈的情况下才应该使用它,并且这被证明会产生显着差异。此外,如果在某个特定平台上它确实产生了显着差异,那么当编译器优化器可以看到它是等效的时,它可能已经替换了按位运算。


4
投票

默认情况下,您应该使用最能表达您的意图的操作,因为您应该优化可读代码。 (今天大多数时候最稀缺的资源是人类程序员。)

因此,如果提取位,则使用&;如果测试可分性,则使用%,即值是偶数还是奇数。

对于无符号值,两个操作都具有完全相同的效果,并且您的编译器应足够智能以通过相应的位操作替换除法。如果您担心,可以检查它生成的汇编代码。

不幸的是,整数除法在有符号值上略微不规则,因为它向零舍入,%的结果根据第一个操作数改变符号。另一方面,位操作总是向下舍入。因此编译器不能仅通过简单的位操作来替换除法。相反,它可以调用例程进行整数除法,或者用位操作替换它,并使用额外的逻辑来处理不规则性。这可能取决于优化级别以及哪些操作数是常量。

这种零不规则甚至可能是一件坏事,因为它是非线性的。例如,我最近遇到过一种情况,我们使用来自ADC的有符号值的除法,它必须在ARM Cortex M0上非常快。在这种情况下,最好用右移替换它,既可以提高性能,也可以摆脱非线性。


2
投票

在“性能”方面,C运营商无法进行有意义的比较。在语言层面上没有“更快”或“更慢”的操作符这样的东西。只能对生成的编译机器代码进行性能分析。在您的具体示例中,生成的机器代码通常完全相同(如果我们忽略了第一个条件因某种原因包含后缀增量的事实),这意味着在任何情况下都不会有任何性能差异。


0
投票

以下是两个选项的编译器(GCC 4.6)生成的优化-O3代码:

int i = 34567;
int opt1 = i++ & 1;
int opt2 = i % 2;

为opt1生成的代码:

l     %r1,520(%r11)
nilf  %r1,1
st    %r1,516(%r11)
asi   520(%r11),1

生成的opt2代码:

l     %r1,520(%r11)
nilf  %r1,2147483649
ltr   %r1,%r1
jhe  .L14
ahi   %r1,-1
oilf  %r1,4294967294
ahi   %r1,1
.L14: st %r1,512(%r11)

所以4个额外的指令......对于prod环境来说什么都不是。这将是一个过早的优化,只是引入复杂性


-2
投票

按位运算要快得多。这就是编译器将为您使用按位运算的原因。实际上,我认为实现它会更快:

~i & 1

同样,如果你看看你的编译器生成的汇编代码,你可能会看到像x ^= x而不是x=0这样的东西。但是(我希望)你不会在你的C ++代码中使用它。

总而言之,做自己,以及需要维护代码的人,一个忙。使代码可读,让编译器进行这些微优化。它会做得更好。

© www.soinside.com 2019 - 2024. All rights reserved.