"if if"与"if else if"的性能差异

Syn*_*ror 29 c c++

我只是想在C/C++中的两个语句之间有任何性能差异:

情况1:

if (p==0)
   do_this();
else if (p==1)
   do_that();
else if (p==2)
   do_these():
Run Code Online (Sandbox Code Playgroud)

案例2:

if(p==0)
    do_this();
if(p==1)
    do_that();
if(p==2)
    do_these();
Run Code Online (Sandbox Code Playgroud)

der*_*ert 34

假设简单类型(在这种情况下,我使用int)并且没有有趣的业务(没有重新定义operator = for int),至少在AMD64上使用GCC 4.6,没有区别.生成的代码完全相同:

0000000000000000 <case_1>:                                   0000000000000040 <case_2>:
   0:   85 ff                   test   %edi,%edi               40:   85 ff                   test   %edi,%edi
   2:   74 14                   je     18 <case_1+0x18>        42:   74 14                   je     58 <case_2+0x18>
   4:   83 ff 01                cmp    $0x1,%edi               44:   83 ff 01                cmp    $0x1,%edi
   7:   74 27                   je     30 <case_1+0x30>        47:   74 27                   je     70 <case_2+0x30>
   9:   83 ff 02                cmp    $0x2,%edi               49:   83 ff 02                cmp    $0x2,%edi
   c:   74 12                   je     20 <case_1+0x20>        4c:   74 12                   je     60 <case_2+0x20>
   e:   66 90                   xchg   %ax,%ax                 4e:   66 90                   xchg   %ax,%ax
  10:   f3 c3                   repz retq                      50:   f3 c3                   repz retq 
  12:   66 0f 1f 44 00 00       nopw   0x0(%rax,%rax,1)        52:   66 0f 1f 44 00 00       nopw   0x0(%rax,%rax,1)
  18:   31 c0                   xor    %eax,%eax               58:   31 c0                   xor    %eax,%eax
  1a:   e9 00 00 00 00          jmpq   1f <case_1+0x1f>        5a:   e9 00 00 00 00          jmpq   5f <case_2+0x1f>
  1f:   90                      nop                            5f:   90                      nop
  20:   31 c0                   xor    %eax,%eax               60:   31 c0                   xor    %eax,%eax
  22:   e9 00 00 00 00          jmpq   27 <case_1+0x27>        62:   e9 00 00 00 00          jmpq   67 <case_2+0x27>
  27:   66 0f 1f 84 00 00 00    nopw   0x0(%rax,%rax,1)        67:   66 0f 1f 84 00 00 00    nopw   0x0(%rax,%rax,1)
  2e:   00 00                                                  6e:   00 00 
  30:   31 c0                   xor    %eax,%eax               70:   31 c0                   xor    %eax,%eax
  32:   e9 00 00 00 00          jmpq   37 <case_1+0x37>        72:   e9 00 00 00 00          jmpq   77 <case_2+0x37>
  37:   66 0f 1f 84 00 00 00    nopw   0x0(%rax,%rax,1)
  3e:   00 00 
Run Code Online (Sandbox Code Playgroud)

case_1末尾的额外指令仅用于填充(以使下一个函数对齐).

这并不奇怪,确定p没有改变,该功能是相当基本的优化.如果p可以改变(例如,通过引用传递或指向各种do_…函数的指针,或者是引用或指针本身,那么可能存在别名)那么行为就不同了,当然生成的代码也是如此.


Mic*_*ker 21

在前一种情况下,不评估匹配的一个条件.

  • 不要高估编译器,他们甚至不知道代码是否具有相同的含义. (5认同)
  • 这个男人说实话.在最糟糕的情况下,它们将是相同的.否则if-else-if会更快,因为只要满足一个条件,就不会评估剩余的条件. (3认同)
  • @xbonez:永远不要低估编译器...如果两段代码都以相同的程序集结束(在C中,`==`不能重载),我不会感到惊讶,因为编译器只能推断出其中一个条件可以为真(假设*if-branch*不修改被测试的值)也就是说,假设两个版本的结果相同且类型是基本类型,编译器可以将两者都转换为开关. (2认同)
  • 好点 - 如果允许每个块修改`p` ...(并尝试证明指向`p`的指针尚未存储在某个磁盘上并且可以被这些函数检索). (2认同)

Waq*_*leh 12

如果别人更快 ; 如果在最后一个之前找到匹配,那么至少跳过最后一个if语句,如果首先找到匹配,它将跳过所有其他语句.

如果慢的话 ; 即使使用第一个if语句找到匹配项,它也将继续尝试匹配其他语句.

  • 什么?在这个之前2年写的接受的答案已经表明编译器意识到如果`if(p == 0)`为真,`p`也不能有其他值,所以跳过那些测试. (3认同)
  • @BoPersson我的答案是合乎逻辑的,不能像接受的答案那样适用于单个特定的编程语言或硬件。 (2认同)

小智 8

是的,性能差异是:

第二个陈述评估每个IF


Mat*_* M. 5

正如已经证明的那样……它有所不同。

如果我们谈论像这样的原始(内置)类型int,那么编译器可能足够聪明,因此它并不重要(或不重要)。但无论如何,对性能的影响很小,因为调用函数的成本比调用 a 的成本高得多if,因此如果您尝试测量它,差异可能会在噪音中消失。

然而,语义却截然不同。

当我读到第一个案例时:

if (...) {
  // Branch 1
} else if (...) {
  // Branch 2
}
Run Code Online (Sandbox Code Playgroud)

然后我知道无论这两个分支会做什么,都只能执行其中一个。

然而,当我读到第二个案例时:

if (...) {
}
if (...) {
}
Run Code Online (Sandbox Code Playgroud)

然后我必须想知道是否有可能两个分支都被采用,这意味着我必须仔细检查第一个分支中的代码以确定它是否可能影响第二个测试。当我最终断定事实并非如此时,我咒骂那个该死的开发人员,他懒得写那些该死的东西else,这本来可以节省我最后 10 分钟的审查时间。

所以,帮助你自己和你未来的维护者,集中精力让语义正确和清晰。

在这个主题上,有人可能会争辩说,也许这种调度逻辑可以用其他结构更好地表达,例如 aswitch或可能是map<int, void()>? (谨防后者并避免过度设计;))