【问题标题】:C++ Conditional operator performanceC++ 条件运算符性能
【发布时间】:2012-09-07 15:24:29
【问题描述】:

我有一个条件语句 expensive_foo() 在 99.9% 的情况下是错误的。我有一个条件语句bar,这在大约 50% 的情况下是正确的。

如果两个陈述都是真的,我希望采取一些行动。所以我几乎可以肯定知道expensive_foo() 是假的,我只想在bar 是真的时检查它。

仅当bar 为真时,下面的代码是否会检查expensive_foo()?还是每次都会检查expensive_foo()?

if ( bar && expensive_foo() )
{
    ...
}

或者我需要做一个这样的结构:

if ( bar )
{
    if ( expensive_foo() )
    {
        ...
    }
}

【问题讨论】:

  • 逻辑布尔运算符短路,除非重载,也就是 yes,foo 只有在 bar 为真时才会被检查。不过,我不得不承认,我没有看到保存单个布尔比较对性能的好处..
  • 好吧,提出这个问题会很聪明。
  • 那么,你是bar && heavy_method_yielding_foo()还是foo = heavy_method(); if(bar && foo) ...?
  • 这些都是重要的细节,没有这些,回答这个问题基本上是不可能的(或者答案不是你真正想要的)。
  • 因为在您修改后,它不再适用。然后你编辑回来。

标签: c++ performance conditional conditional-statements


【解决方案1】:

逻辑与运算符&& 是短路的,这意味着当且仅当第一个操作数被评估为true 时,才能保证第二个操作数被评估。 if (bar && foo) 和 if (bar) if (foo) 的条件相同。

鉴于foo 的计算成本很高,您几乎绝对应该首先检查bar。尽管分支预测器无法很好地预测 bar,但与 foo 的计算相比,这只是一个很小的影响。

因此结构应该是:

if (bar)
{
    if (bool const foo = compute())   // expensive
    {
        // ...
    }
}

请注意,所有这些结构都意味着我们可能会或可能不会调用compute()。如果您需要无条件调用函数,那么您应该将其设为 first 检查。在这种情况下,应该利用对结果的成功分支预测。

【讨论】:

  • 这只是语义上的,而不是性能方面的。如果 bar 和 foo 没有副作用,编译器仍然可以重新排序它们,从而导致性能损失。
  • @usr:我很确定它不能。
  • @DeadMG 编译器可以根据标准做任何不改变程序行为的事情。如果没有观察到行为变化,编译器可以做到这一点。 (OP 编辑​​了帖子,虽然告诉我们 foo 实际上是 foo_expensive()。这让我的观点没有实际意义,尽管它是正确的)。
  • @DeadMG 取决于。如果它们有副作用,则编译器无法重新排序副作用。否则,您无法观察到差异。
  • @Kolyunya:编号if (bar && compute()) 与if (bar) { if (compute()) { /* .. */ } } 相同。这是安全和好的。我只是想你可能想存储compute() 的结果,或者有一个else 分支。但如果您不这样做,请随意将所有内容归为一个条件。
【解决方案2】:

只有在bar 为真的情况下,这两个代码才会检查foo。在第一种情况下,这是由语言惰性布尔表达式求值来保证的(实际上可以在某些编译器中显式关闭,但默认情况下是开启的)。

【讨论】:

  • 如果任何编译器允许您关闭短路评估,我会感到惊讶;这会破坏很多代码。
  • 你可以通过说bar & foo来关闭它。
【解决方案3】:

其他答案都不错,但我在质疑前提。

如果您查询的谓词在 99.9% 的情况下为假,这类似于在 2000 个条目的列表中进行线性搜索,您要查找的项目可能以大致相等的概率出现在任何地方。

在这种情况下,通常人们会尝试以不同的方式组织列表,以便进行 O(logN)(或更好)搜索而不是 O(N)。 我不知道你能不能做到这一点,但这是我要关注的地方。

当以高度偏斜的概率提出问题时,如果可以消除偏斜,这可能是一个很大的加速机会。 (假设大部分时间都花在了这个活动上。如果不是,那么这一点就没有实际意义了。)

【讨论】:

    【解决方案4】:

    如果两者的优化装配不完全相同,我会感到惊讶。如果 bar 为 false,则永远不会调用昂贵的 foo()。

    【讨论】:

      【解决方案5】:

      我看到一个评论说编译器可以重新排序操作,我倾向于这个观点。 改进顺序程序的自动并行化的努力已经产生了更好的编译器,它帮助程序员避免从他们的程序中进行复杂的并行化例程。 编译器可以重新排序操作,只要重新排序不违反/恶化局部性,或者在广义上不违反程序的实际意图。

      但我怀疑编译器会尝试重新排序条件语句 喜欢:

        if ( bar && expensive_foo() )
      

      如果把成本较低的布尔函数放在开头,肯定可以节省一些时间。

      【讨论】:

        猜你喜欢
        • 2011-03-21
        • 1970-01-01
        • 2017-05-22
        • 1970-01-01
        • 2013-07-15
        • 2015-07-12
        • 2023-04-03
        • 2010-10-26
        • 2021-04-22
        相关资源
        最近更新 更多