比较if(...)和?:哪个更好?

时间:2013-02-28 08:31:40

标签: c++ c performance optimization compiler-construction

我对以下代码段的流程有一个简单的疑问。我在高级和汇编指令级别比较了这个代码块。我发现?:远比分支好。

const int THUMB_IMAGE = 0;
const int ICON_IMAGE = 1;
const int UNKNOWN_IMAGE = 2;

void foo( int nFlag ){
    int CopyFlag = nFlag;
    if( CopyFlag == THUMB_IMAGE )
        CopyFLag = ICON_IMAGE; // Setting THUMB and ICON images to same level
    // Executing rest of the code
}

void foo1( int nFlag ){
    int CopyFlag = ( nFlag == THUMB_IMAGE ) ?
                     ICON_IMAGE : nFlag; // Setting THUMB and ICON images to same level
    // Executing rest of the code
}

int main(void){
   foo( THUMB_IMAGE );
   foo1( THUMB_IMAGE );
   return 0;
}

在上面的代码段中,有两个函数foo()foo1()。这两个函数将两种图像类型设置为ICON_IMAGE

问题是如何实施作业和if()

哪些条件语句经过高度优化,if()或三元运算符?:

在汇编代码级别,要翻译if()CMP(分支)和MOV指令是必要的。 对于?:运算符,我认为有一些特殊指令,但完全避免了分支指令。

谁能说出哪种方法最好?

foo()中,无论if条件如何,第一个赋值操作都会完成。它可能不是一直需要的。

但是在foo1()中,这是在?:运算符部分完成的,可以避免不需要的赋值操作。我想知道foo()foo1()中的代码是否已经过优化?

9 个答案:

答案 0 :(得分:2)

如果没有优化,您将设置copyFlag两次 您使用if时只编写一次?。相当于 使用?:的{​​{1}}将是:

if

通过优化,我希望生成所有三种变体 或多或少相同的代码。

一般来说,你不应该担心这种事情。以上 所有,我不担心汇编程序;相同 两种情况都可以进行优化。 (有可能 写这个没有任何分支指令;不管那是不是 最好的解决方案是由编译器来决定,而不是你。) 优化器会处理它。你应该写代码 以最清晰的方式 - 在这种情况下,这意味着 三元运算符。

答案 1 :(得分:1)

在一个不错的编译器上,if?:之间的效率绝对没有区别。

这两种做同样事情的方法之间的唯一区别是,?:包含微妙的促销规则。第二个和第三个表达式相互平衡,就好像它们是相同操作的操作数一样。如果您运气不好,这可能会导致错误,但可能不会使?:的效率低于if

例如,如果您编写int x = 1;然后x ? 1 : 1.0f;,则结果类型为float,这可能是意料之外的。

答案 2 :(得分:1)

对于 x86 系列,优化为off的编译器会将if... else...转换为带有 x86 中跳转指令的分支,并将...?...:...转换为有条件移动 x86 中的说明。

如果您在代码中直接编写条件,那么优秀的编译器会将分支优化为条件移动。

对于性能,如果很容易预测分支会更好,如果很难预测,条件移动会更好。原因是如果预测不正确,分支指令会在现代处理器中产生显着的分支预测损失,而条件移动指令的延迟稍微慢一些,无论条件是否成立。

有关详细解释,请参阅我的回答here

答案 3 :(得分:1)

我100%确定任何现代编译器都会优化这两者。当然,在这个特定的例子中,编译器可能会删除foofoo1的所有代码,因为这些在这个例子中什么都不做,除非你关闭优化 - 这不是一个公平的比较。您需要一个更复杂的示例,其中编译器无法确定输入并需要代码的结果。

现在,如果我对这段代码的作用是正确的 - 即复制某种图像 - 那么我希望函数开头的if语句对于这个代码花费的总时间非常少功能。

与性能一样,首先分析您的代码并识别“热点”,然后集中精力使热点运行得更快。我很确信这个不是热点。因此,您正在尝试优化不会对代码产生重大影响的内容。

此外,不要相信互联网上有人告诉你哪个版本的代码更快 - 在不同的配置中运行它然后做出决定。最好是在具有不同硬件的几台机器上(不同的制造商处理器,在这种特殊情况下) - 除非它是一个只需要在当前计算机上快速运行的爱好项目。

答案 4 :(得分:0)

这取决于编译器。一些编译器将优化两个语句并从中生成相同的汇编代码。

作为旁注,使用最快的解决方案的优势非常小,不值得麻烦。如果您调用此代码来设置图像的类型,则很可能您将在某处使用该图像(即使只更改图像类型而不显示它们),这需要很多更多的CPU周期而不是if?:语句中的额外汇编指令。使用三元运算符?:编写代码可能会使开发和维护更加困难,而且不值得付出努力。

编辑: 性能上的差异来自于foonFlag时您在THUMB_IMAGE中执行两项作业的事实。以下foo2应该与foo1的性能更接近。

void foo2( int nFlag ){
   int CopyFlag;
   if( nFlag == THUMB_IMAGE )
       CopyFlag = ICON_IMAGE; // Setting THUMB and ICON images to same level
   else
       CopyFlag = nFlag;
   // Executing rest of the code
}

性能上的差异可能是编译器未构建以优化赋值,而if语句将同一变量赋值为if-else语句(例如Sun / Oracle) Java编译器这样做)或者可能已经指示编译器不要优化代码(通过编译标志/参数)。

答案 5 :(得分:0)

理论上应该没有任何区别,理论上,无所不知的优化器会将其优化为相同的生成代码。

在实践中,我知道至少amd64上的某些gcc版本会生成if的分支,并使用cmov指令与:?无关,无论优化级别和标志如何。听起来像一个小东西,但在代码库中,我在一个类似于此的构造上测试它,在一种锁中使用if更改为:?使cpu使用率减少了10%在宏观基准上。

当然,您的里程可能会有所不同,具体取决于编译器,编译器版本,标志和您控制之外的其他内容。因此,请对您的编译器进行测试,看看它是否值得优化。

答案 6 :(得分:0)

使用优化编译器,您不应该看到任何差异。只需使用更具可读性的东西。

答案 7 :(得分:0)

所有其他答案,表明一个体面的编译器不关心,并且您可能不应该担心,除非您在分析器中看到此函数作为性能瓶颈弹出,是完全正确的。

但是......如果你在一些分支机构很昂贵的架构上,并且条件移动不存在,或者你的编译器非常糟糕......你可以将天真版本与以下内容进行比较:

void foo( int nFlag ){
    int CopyFlag = ((nFlag & 2) << 1) + 1;
    // Rest of code...

显然需要提出很多警告(它很丑陋,黑客,不可维护等等)......但它会并行化!

我真正得到的是,不是担心两个相似语言结构的确切代码,你应该(在确定你甚至有性能问题之后)看看算法本身,以及是否是否可以更改或替换它以避免完全出现问题。

答案 8 :(得分:0)

只有在关闭优化时才会存在性能差异。现代编译器应该平等地优化两个版本。

选择其中一种解决方案的原因可能是:

  1. 可读性。但是,个人偏好可能会有所不同。
  2. 调试版本性能。但是,差异应该很小。
  3. 易于调试。不可能在三元运算符的某个分支处设置断点(除非你进入汇编语言级别,否则有时可能)。
  4. 覆盖率分析。如果您编写测试并收集覆盖率分析信息,if语句可以让您了解两个分支是否都经过测试。三元运算符没有。其中一个表达式在程序运行期间根本无法评估,但代码覆盖率工具将向您显示所有内容。