【问题标题】:Does the order of expression to check in boolean statement affect performance在布尔语句中检查的表达式顺序是否会影响性能
【发布时间】:2010-09-15 17:06:14
【问题描述】:

如果我有一个布尔表达式要检查

(A && B)

如果发现 A 是假的,语言会费心检查 B 吗?这是否因语言而异?

我问的原因是我想知道是否即使 A 为假,B 也会被检查,那么不会

if (A) {
  if(B) {

  } else {
     // code x
  }
} else {
  // code x
}

略快于

if (A && B) {

} else {
   // code x
}

【问题讨论】:

  • 这不是一个与语言无关的问题。这看起来像一个 C 系列语言,在这种情况下它是一个短路运算符。

标签: optimization conditional boolean


【解决方案1】:

这取决于语言。大多数语言会将A && B 实现为短路运算符,这意味着如果A 评估为false,则永远不会评估B。有一个detailed list on Wikipedia

【讨论】:

    【解决方案2】:

    几乎每一种语言都实现了称为short-circuit evaluation 的东西,这意味着是的,如果A 为假,(A && B) 将不会评估B。如果你这样写,这也会生效:

    if (A || B) {
        ...
    }
    

    A 为真。如果 B 可能需要很长时间才能加载,这一点值得记住,但通常无需担心。

    作为一段历史,在我看来这是 LISP 的一个痛点,因为这样的代码:

    (if (and (= x 5) (my-expensive-query y)) "Yes" "No")
    

    不是由函数组成,而是所谓的“特殊形式”(即“and”在这里不能被定义)。

    【讨论】:

      【解决方案3】:

      这将 100% 取决于该语言如何编译所述代码。 一切皆有可能 :)

      您是否想知道某种特定的语言?

      【讨论】:

        【解决方案4】:

        简而言之,没有。双分支涉及各种形式的分支预测。如果 A 和 B 易于评估,则以非短路方式执行 if (A && B) 可能比 if (A) if (B) 更快。此外,您还复制了第二种形式的代码。这几乎总是(每条规则都例外..我猜)很糟糕,而且比任何收益都要糟糕。

        其次,这是您对语言解释器、JIT 或编译器进行的一种微优化。

        【讨论】:

        • 我在考虑 B 是一个非常复杂的条件的实例(在我正在开发的应用程序中,它们之间的 || 运算符大约有 5 或 6 个不等式,它被调用页面加载很多次)...但是由于 javascript 使用短路,它看起来不会成为问题
        • 短路评估中操作数的顺序通常不是微优化。有时它在语义上很重要(例如 C 程序中的 p && p->a),有时它对性能很重要。如果 A 很快并且通常返回 false,并且 B 需要很长时间来计算,则 A && B 可能比 B && A 快得多,并且由于存在语义差异,编译器通常无法重新排序它们。跨度>
        • 如果你使用 C,为什么不直接使用 if (type* p = some_init()) { p->a; }
        • @DeadMG:因为你写的和p && p->a在语义上不一样?语句和表达式不一样。 p && p->a 是一个表达式,如果 p 为 null 或 p->a 为 0,则该表达式将产生 0,否则为 1。如果引用 p->a 的唯一原因是副作用,那么您的声明将有效,通常情况并非如此。
        • @David Thornley:这个想法显然是你可以在 if 块中放置任何表达式,并且 p 仅在以更简洁和更简单的方式有效时使用。
        【解决方案5】:

        许多语言(包括几乎所有花括号语言,如 C/C++/Java/C#)都提供短路布尔求值。在这些语言中,如果 A 为假,则不会评估 B。您需要查看(或询问)这是否适用于您的特定语言,或者是否有办法做到这一点(例如,VB 有 AndAlso)。

        如果您发现您的语言不支持它,您还需要考虑评估 B 的成本是否值得维护两段相同的代码 - 以及缓存占用量可能翻倍(更不用说所有额外的分支)每次都进行重复。

        【讨论】:

          【解决方案6】:

          正如其他人所说,这取决于语言和/或编译器。对我来说,我不在乎短路的速度有多快或多慢,复制代码的需要是一个交易杀手。

          如果 A 和 B 实际上是具有副作用的调用(即它们不仅仅是返回一个适合比较的值),那么我认为应该将这些调用转换为变量赋值,然后在你的比较中使用.无论您是始终需要这些副作用还是仅有条件地需要它们都没有关系,如果您不依赖于是否存在短路,代码将更具可读性。

          关于可读性的最后一点是基于我的感觉,即减少参考外部文档的需要可以提高可读性。阅读一本包含一堆需要查字典的新单词的书,比阅读同一本书要困难得多,因为你已经有了必要的词汇。在这种情况下,短路是不可见的,因此任何需要查找它的人甚至都不知道他们需要查找它。

          【讨论】:

          • 我以 C 为例。如果 A 是一个将指针值分配给 p 的表达式,它可能是有效的或空指针,而 B 是一个使用 p 并具有其他副作用的表达式,那么您不能简单地计算 A 和 B 然后使用他们的真值。短路也不是语言中晦涩难懂的部分,因此无需避免混淆,以免混淆。
          • @David Thornley:在您的示例中,您可以在继续 B 之前轻松检查 A 的返回值是否有效。我对通过短路进行条件评估的担忧与许多人的担忧形成鲜明对比使用条件运算符 (?:)。至少它有一个明确的语法,不像短路。对于来自不提供条件运算符的语言的人来说,检查文档的需要是显而易见的。短路没有提供这样的指示。 FWIW,我可能有偏见,因为我来自一种两者都没有的语言,只有短路让我绊倒了。
          • 我坚持我之前的立场:短路运算符是 C 的一部分,并且通常被用作这样。任何有能力的 C 程序员都会正确地使用它,而任何能胜任其他多种语言的程序员都会期待短路行为。我不同意建议不要使用它,因为真正的新手可能会感到困惑。
          • @David Thornley:你说服了我。碰巧,当人们告诉我应该避免使用条件运算符时,这与我使用的论点几乎相同:) 谢谢。
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2011-02-18
          • 2018-04-15
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多