我已经看到了两种不同的方法来组合位移和屏蔽,并且想知道它们是否相同,假设结果被用作布尔值,或者如果一个问题比另一个问题更少或性能更好。
这两个:
flags & (BIT_MASK << BIT_NUMBER)
或
(flags >> BIT_NUMBER) & BIT_MASK
前者似乎可能在某些平台上存在问题,具体取决于flags
的大小,即位移可能会将掩码推出临时变量的顶部。这是一个问题吗?升档与降档有任何性能差异吗?
将BIT_MASK
和BIT_NUMBER
合并到一个明确的掩码中再次让我感觉更好,但我正在使用遗留代码,我希望尽量减少对其的更改。
答案 0 :(得分:1)
如果BIT_MASK
和BIT_NUMBER
是常量,编译器可能会预先计算表达式,并在将它们组合在一起时保存指令。这有利于第一种方法。
如果你试图获得多于一位,你会想要将结果转移到右边。这有利于第二种方法。
答案 1 :(得分:1)
flags & (BIT_MASK << BIT_NUMBER)
可以在一个位和指令中完成。
(flags >> BIT_NUMBER) & BIT_MASK
需要两条指令,一个转换后跟一个位和。
(实际上,在PowerPC上,第二个版本也有一个专用的操作码,rlwinm
,但英特尔没有这样的运气。)
当然,最大的区别在于它们会产生不同的结果值。如果您只检查零或非零,那么我更喜欢第一个版本,因为它只是一条指令。
答案 2 :(得分:0)
我建议你不要考虑编译器后端可能会做什么,如果你没有使用优化然后它没关系,如果你使用优化,那么任何好的编译器都会做正确的事情尽可能少的指令用于编译目标。
根据语言在语义上的含义来思考。例如,如果flags类型为uint64_t
且BIT_NUMBER
大于31,那么只有第二种形式可能会执行您想要的操作。原因是(BIT_MASK << BIT_NUMBER)
将被评估为int
,可能是32位,并且因为BIT_NUMBER
大于31将评估为零,因此整个事物将在之后产生零。和。另一方面,(flags >> BIT_NUMBER)
将被评估为64位表达式(因为标志是64位),并且在进行AND之前将被正确移位。
有关Clang编译器的有趣讨论,请参阅Clang: Defending C++ from Murphy's Million Monkeys,这将向您展示当今编译器中可能存在的语义理解的深度,特别是查看解决C ++整数算术规则细微差别的分钟21:25
我在上面提到的。这是谈话中使用的示例
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位数量的旋转,并且会产生旋转说明,见Are bitwise rotations slower than shifts on current Intel CPU?。这些天编译器非常好。