C 位掩码检查是否比比较数字更有效?

C 位掩码检查是否比比较数字更有效?,c,performance,compiler-optimization,C,Performance,Compiler Optimization,我听说基于位掩码的检查比比较数字更有效。这是真的吗 “基于位掩码”检查: 数字比较: if (val == IMPORTANT_VAL) ... 在这两种情况下,我觉得无论如何它都必须从内存中取出val。那么为什么位掩码更有效呢?编译器是否对位掩码做了一些特殊的处理?因此,位字段的好处在于,您可以将大量布尔标志打包到一个单词中。测试一个特定标志的代码是可比较的,但是测试标志的特定子模式的代码可以短得多,尽管您可能需要自己编写测试: // using bool bool a,b,c,d; if

我听说基于位掩码的检查比比较数字更有效。这是真的吗

“基于位掩码”检查:

数字比较:

if (val == IMPORTANT_VAL)
...

在这两种情况下,我觉得无论如何它都必须从内存中取出
val
。那么为什么位掩码更有效呢?编译器是否对位掩码做了一些特殊的处理?

因此,位字段的好处在于,您可以将大量布尔标志打包到一个单词中。测试一个特定标志的代码是可比较的,但是测试标志的特定子模式的代码可以短得多,尽管您可能需要自己编写测试:

// using bool
bool a,b,c,d;
if (a && !b && c && !d) ...

// hoping the compiler knows what we are doing
enum { A= 0x01, B= 0x02, C= 0x04, D= 0x08};
if ((flags&A) && !(flags&B) && (flags&C) && !(flags&D)) ...

// Optimise it ourselves:
enum { A= 0x01, B= 0x02, C= 0x04, D= 0x08};
if ((flags & (A|B|C|D)) == (A|!B|C|!D)) ...
在第一种情况下,编译器必须加载每个位置,尽管它可能会提前退出。在后一种情况下,它至少可以加载一次值并在核心中执行多个操作。一个好的优化者可以将第二个模式优化到第三个模式,即2条指令,尽管优化者可能也意识到它可以将所有bool作为“长”加载以减少带宽,并且它们至少都在同一个缓存线中,这几乎和现在在核心中一样好

但是在任何情况下,为了简洁起见,第三种形式将战胜第一种形式,同时节省一点存储空间

请注意,您的两个示例测试的不是同一件事

A&5
测试4位和1位是否都已设置,但完全忽略2位和任何更高的位


A==5
测试是否设置了1位和4位,但它也检查所有其他位是否清晰。

首先,我们通过假设
重要位==重要位
VAL
的所有其他位都是
0
来限制这个问题,以便两个测试的结果相同

简短回答:别担心

长答案:

它取决于目标体系结构、重要位的值以及执行代码的上下文

x86有
指令,可以方便地设置
Z
标志,因此可以使用它在单个指令中屏蔽和测试一个值与0的对比。然后是
CMP
指令,它比较值。
以及
CMP
指令都具有相同的延迟1和吞吐量4。因此,在x86上不会有任何区别,除非编译器能够设法重用某些特定值并与上面/下面的代码混合。不过,性能上的差异将微乎其微

话虽如此,
&
==
测试的语义确实不同

  • if(val&…
    test用于屏蔽位
  • if(val==…
    test用于比较值

我建议首先从代码可读性的角度来处理它。如果
val
不是一个位字段,请帮自己一个忙,不要使用
&
来对抗它。

我想这可能取决于编译器/CPU,尽管我希望在大多数情况下,它们在性能方面非常相似。位掩码检查当多个标志(位)组合成一个整数(字节、短、长)时,
&
非常常见因为这样可以很容易地检查是否设置了单个位。您可以。这里,第一种方法需要3条指令,但第二种方法是唯一的,即使假定
重要位
重要位
相同,这两种测试也完全不同。如果重要位的值为
1
,则第一种测试检查w
val
中的数字是否为奇数,而第二个则检查它是否等于
1
。这些数字完全不同,因此比较是不相关的。我选择了最简单的情况,但不管
重要位的实际值是多少,这个概念都适用。
重要位的值是多少是
val
的可能值吗?如果它们不执行相同的操作,比较是没有意义的。无论如何,你不会注意到ALU中这样一个琐碎的操作有任何区别。它们做的是语义不同的事情,因此它是学术性的,取决于机器的指令集。它不可能是一个值能够优化,大多数编译器完全能够提供优化,这将淹没这种“微观优化”可能带来的任何好处。专注于高效的软件设计和适当数据结构的选择,让编译器处理这种依赖于机器的东西。
// using bool
bool a,b,c,d;
if (a && !b && c && !d) ...

// hoping the compiler knows what we are doing
enum { A= 0x01, B= 0x02, C= 0x04, D= 0x08};
if ((flags&A) && !(flags&B) && (flags&C) && !(flags&D)) ...

// Optimise it ourselves:
enum { A= 0x01, B= 0x02, C= 0x04, D= 0x08};
if ((flags & (A|B|C|D)) == (A|!B|C|!D)) ...