【问题标题】:Are bitmask checks more efficient than comparing numbers?位掩码检查比比较数字更有效吗?
【发布时间】:2020-01-06 15:45:34
【问题描述】:

有人告诉我,基于位掩码的检查比比较数字更有效。这是真的吗?

“基于位掩码”检查:

if (val & IMPORTANT_BIT)
...

数字比较:

if (val == IMPORTANT_VAL)
...

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

【问题讨论】:

  • 我猜这可能取决于编译器/CPU,尽管我希望在大多数情况下它们在性能方面非常相似。当多个标志(位)组合成一个整数(字节、短、长)时,使用& 进行位掩码检查非常常见,因为这样可以轻松检查是否设置了单个位。
  • 您可以test this yourself。在这里,第一种方法需要 3 条指令,但第二种方法 - 只有一个
  • 即使假设IMPORTANT_BITIMPORTANT_VAL 相同,这两个测试也完全不同。如果重要位的值为1,则第一个测试检查val 中的数字是否为奇数,而第二个测试是否等于1。这些完全不同,以至于比较是无关紧要的。我选择了最简单的情况,但无论IMPORTANT_BIT 的实际值如何,这个概念都适用。
  • IMPORTANT_* 的值是多少,val 的可能值是多少?如果他们没有执行相同的操作,那么比较是没有意义的。无论如何,您不会注意到 ALU 中这种微不足道的操作有什么不同。
  • 它们在语义上做不同的事情,所以它是学术性的并且依赖于机器的指令集。这不太可能是一个有价值的优化,大多数编译器都能够提供优化,这些优化将淹没从这种“微优化”中获得的任何可能的好处。专注于高效的软件设计和选择合适的数据结构,让编译器处理这种依赖机器的东西。

标签: c performance compiler-optimization


【解决方案1】:

所以位域的好处在于你有很多布尔标志,你可以将它们打包成一个单词。测试一个特定标志的代码是可比较的,但测试一个特定的标志子模式的代码可能要短得多,尽管您可能需要自己编写测试:

// 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 加载为“long”以减少带宽,并且它们至少都在同一个缓存行中,这几乎和现在的核心一样好。

但无论如何,为简洁起见,第 3 种形式将胜于第一种形式,并节省一点存储空间。

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

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

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

【讨论】:

    【解决方案2】:

    首先,我们通过假设IMPORTANT_BIT == IMPORTANT_VALval 的所有其他位是0 来限制问题,因此两个测试的结果是相同的。

    简答:别担心。

    长答案:

    这取决于目标架构、IMPORTANT_BIT 的值以及执行代码的上下文。

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

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

    • if (val & ... 测试用于屏蔽位。
    • if (val == ... 测试用于比较值。

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-04-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-05-06
      • 2023-03-09
      • 2010-09-13
      • 1970-01-01
      相关资源
      最近更新 更多