性能明智的是Bitwise算子与法向模量有多快

Mav*_*ven 31 c++ bit-manipulation bitwise-operators

if(i++ & 1) {

}
Run Code Online (Sandbox Code Playgroud)

for

if(i % 2) {

}
Run Code Online (Sandbox Code Playgroud)

可以使用if在正常流量或条件语句类似操作for,if等提高性能,这将是for在可能的地方总是使用它们?

Jer*_*fin 38

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

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

  • 如果所有值都是有符号整数类型,那么表达式'a = b&1;`将在每个符合标准的实现上,我知道它比`a = b%2;`更快,因为后面的表达式相当于`a = b <0? - (b&1):b&1;`.如果对结果做的唯一事情就是测试零,那么优化器可能能够识别出`b <0`和`b> = 0`的情况是等价的,但我不会特别期待这种优化. (7认同)

Mat*_* M. 29

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

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

在这个过程中,编译器将应用窥孔优化,其中包括强度降低优化,如(维基百科提供):

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
Run Code Online (Sandbox Code Playgroud)

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

  • 最后一个示例的解释:`x * 15` == `x * (16 - 1)` == `(x * 16) - (x * 1)` == `(x &lt;&lt; 4) - x`。 (4认同)

Ton*_*roy 8

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

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

  • +1强调可读性; 通常优化是在最后阶段,最多我们可以尝试不使我们的代码非常陌生. (3认同)

sta*_*lue 5

默认情况下,您应该使用最能表达您的预期含义的操作,因为您应该针对可读代码进行优化。(今天大部分时间最稀缺的资源是人类程序员。)

因此,&如果您提取位,请使用,并%在您测试可分性(即值是偶数还是奇数)时使用。

对于无符号值,这两种操作具有完全相同的效果,您的编译器应该足够聪明,可以用相应的位操作替换除法。如果您担心,可以检查它生成的汇编代码。

不幸的是,整数除法在有符号值上有点不规则,因为它向零舍入并且 % 的结果根据第一个操作数更改符号。另一方面,位操作总是向下舍入。所以编译器不能只用一个简单的位运算来代替除法。相反,它可以调用整数除法例程,或者用位操作替换它,并带有附加逻辑来处理不规则性。这可能取决于优化级别以及哪些操作数是常量。

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