1

我已经看到了两种不同的方法来组合位移和屏蔽,并且想知道它们是否等效,假设结果被用作布尔值,或者一个问题比另一个问题更少或性能更好。

他们俩:

flags & (BIT_MASK << BIT_NUMBER)

或者

(flags >> BIT_NUMBER) & BIT_MASK

前者似乎在某些平台上可能存在问题,具体取决于 的大小flags,即位移可能会将掩码推出临时变量的顶部。这是一个问题吗?升档和降档有性能差异吗?

BIT_MASK和组合BIT_NUMBER成一个明确的掩码让我再次感到更好,但我正在使用我想尽量减少更改的遗留代码。

4

3 回答 3

2

flags & (BIT_MASK << BIT_NUMBER)可以在一位和指令中完成。

(flags >> BIT_NUMBER) & BIT_MASK需要两条指令,一个移位后跟一个位与。

(其实在PowerPC上,第二个版本也有专门的操作码rlwinm,但是Intel就没有这个运气了。)

当然,最大的区别是它们产生不同的结果值。如果您只检查零或非零,那么我更喜欢第一个版本,因为它是一条指令。

于 2013-02-05T22:05:26.360 回答
1

如果BIT_MASKBIT_NUMBER是常量,编译器可能会预先计算表达式并在将它们组合在一起时保存指令。这有利于第一种方法。

如果您想获得不止一位,您将希望将结果向右移动。这有利于第二种方法。

于 2013-02-05T22:01:21.447 回答
0

我建议您不要考虑编译器后端可能会做什么,如果您不使用优化,那么没关系,如果您使用优化,那么任何好的编译器都会在很少的指令中做正确的事情尽可能为您的编译目标。

考虑它在语言中的语义含义。例如,如果 flags 是 typeuint64_t并且BIT_NUMBER大于 31,那么只有 2nd 形式可能会执行您想要的操作。原因是(BIT_MASK << BIT_NUMBER)它将被评估为int,可能是 32 位,并且由于BIT_NUMBER大于 31 将评估为零,因此整个事物在 AND 之后将产生零。另一方面,(flags >> BIT_NUMBER)将被评估为 64 位表达式(因为标志是 64 位),并且在执行 AND 之前将被正确移位。

有关 Clang 编译器的有趣讨论,请参阅Clang: Defending C++ from Murphy's Million Monkeys这将向您展示当今编译器可能对语义理解的深度,特别是查看21:25我在上面提到的 C++ 整数算术规则的细微差别的分钟. 这是演讲中使用的示例

static const long long DiskCacheSize = 8 << 30; // 8 Gigs

Clang gives:

% clang++ -std=c++11 -fsyntax-only overflow.cpp
overflow.cpp:1:42: warning: signed shift result (0x200000000) requires 35 
bits to represent, but 'int' only has 32 bits [-Wshift-overflow]
static const long long DiskCacheSize = 8 << 30; // 8 Gigs
                                       ~ ^  ~~

最近,我惊讶地发现 gcc 和 Java JIT 编译器足够聪明,可以理解一组移位、掩码和 OR 本质上是对 32 位量进行旋转,例如,将生成旋转指令,请参阅按位旋转是否比当前 Intel CPU 上的移位慢?. 这些天编译器非常好。

于 2013-02-05T22:10:23.923 回答