C/C++编译器会优化这个if语句吗?

问题描述 投票:0回答:7

我有这样的代码,我发现它有点难以阅读:

// code1
if( (expensiveOperation1() && otherOperation() && foo()) 
     || (expensiveOperation2() && bar() && baz()) {
  // do something
}

我只是将其更改为以下内容,以使其更具可读性:

// code2
const bool expr1 = expensiveOperation1() && otherOperation() && foo();
const bool expr2 = expensiveOperation2() && bar() && baz();
if(expr1 || expr2){
   // one of the conditions met
}

但是我现在应该关心效率吗?

我的意思是,在

code1
中,如果第一个连接子句得到满足,那么它甚至不会费心去查看第二个连接子句,因为已经很清楚该陈述将是正确的。

但在我的更具可读性的示例中,

cond1
cond2
都必须计算。或者如果 expr2 没有在其他地方使用,编译器是否足够聪明,可以将我的
code2
更改为
code1

c++ c performance optimization
7个回答
24
投票

我想说不应该,因为如果任何函数有副作用,它们在逻辑上就不等价。

但是,以下内容是等效的,并且它的优点是允许您为测试函数提供描述性名称,使代码更加自文档化:

// code3
inline bool combinedOp1()
{
    return expensiveOperation1() && otherOperation() && foo();
}

inline bool combinedOp2()
{
    return expensiveOperation2() && bar() && baz();
}

然后调用如下:

if (combinedOp1() || combinedOp2())
{
    // do something
}

21
投票

也许吧,但为什么不让你的第二次检查合并第一个呢?

// code3
bool expr = expensiveOperation1() && otherOperation() && foo();
expr = expr || (expensiveOperation2() && bar() && baz());
if(expr){
   // one of the conditions met
}

更好的是,扭转局面,使最便宜的检查首先出现在每个列表中,利用惰性评估完全跳过昂贵的操作。


4
投票

好吧,如果条件有副作用,编译器通常不会对 && 和 || 重新排序。 一些非常聪明的编译器可能能够静态地验证它们的独立性,但这很少见。

如果可能,重新排序条件,让便宜的操作先出现,这样就可以短路昂贵的操作。


2
投票

这里最常见的答案是用“不应该”和“也许”来回答问题!这不是一个明确的答案来吧!

如果您想知道您的编译器是否正在优化这一小段代码,请使用“显示程序集输出”标志来编译您的代码。在 GCC 上,该标志是“-S”。 然后查看输出程序集,它将准确地 100% 显示正在编译或未编译的内容。

然后,您可以将截取的第一个代码与“therefromhere”的代码片段进行比较,并快速尝试大量代码更改,直到找到编译器优化最好的代码(即最少周期)。

查看 asm 输出听起来复杂且可怕,但实际上只需要大约 5 分钟即可完成。我在这里做了一个例子:在 C 中交换值的最快方法是什么?


1
投票

这个问题的答案当然取决于编译器。检查的最终方法是查看编译器为此函数生成的程序集。大多数(全部?)编译器都有办法做到这一点,例如

gcc
-S
选项。如果由于某种奇怪的原因,您的调试器不能向您显示函数的反汇编,或者有其他工具可以执行此操作。


0
投票

很好的答案。

我只想补充一点,我不喜欢编译器如此积极地优化以重新排序我的代码。

我只是想让编译器按照它的指示去做。

如果它能智胜我,它也能智胜自己。


0
投票

如果编译器知道 cond2 中的函数(expectiveOperation2()、bar() 和 baz())是纯函数(即没有副作用),则可以进行优化。如果它们是纯函数,确保编译器知道的最简单方法就是使它们成为内联函数。

即使您不知道,编译器也有可能知道,但这不太可能,因为昂贵的Operation2()可能做了相当多的工作。

FWIW,如果这些函数是纯函数,您可能应该重新排序它们,以便 bar() 和 baz() 在昂贵的Operation2() 之前运行(与 cond1 中的排序相同)。

© www.soinside.com 2019 - 2024. All rights reserved.