我只是在想C / C ++中的两个语句之间是否有任何性能差异:
案例1:
if (p==0)
do_this();
else if (p==1)
do_that();
else if (p==2)
do_these():
案例2:
if(p==0)
do_this();
if(p==1)
do_that();
if(p==2)
do_these();
答案 0 :(得分: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
case_1末尾的额外指令仅适用于padding (to get the next function aligned)。
这并不奇怪,因为该函数中的p没有改变是相当基本的优化。如果p可以改变(例如,通过引用传递或指向各种do_…
函数,或者是引用或指针本身,所以可能存在别名)那么行为就不同了,当然生成的代码也是。
答案 1 :(得分:21)
在前一种情况下,匹配的匹配后的条件不会被评估。
答案 2 :(得分:11)
if else 更快;如果在最后一个之前找到匹配,那么至少跳过最后一个if语句,如果首先找到匹配,它将跳过所有其他语句。
如果 更慢;即使使用第一个if语句找到匹配项,它也将继续尝试匹配其他语句。
答案 3 :(得分:7)
是的,性能差异是:
第二个陈述评估每个IF
答案 4 :(得分:3)
对于如此有限数量的表达式,您可能不会注意到性能的任何差异。但理论上,if..if..if
需要检查每个表达式。如果单个表达式是互斥的,则可以使用if..else if..
来保存该评估。这种方式只有在前面的情况失败时才会检查另一个表达式。
请注意,只检查int是否相等,您也可以使用switch
语句。这样,您仍然可以保持一定程度的可读性,以进行长时间的检查。
switch ( p )
{
case 0:
do_this();
break;
case 1:
do_that();
break;
case 2:
do_these():
break;
}
答案 5 :(得分:2)
正如已经证明的那样......它有所不同。
如果我们讨论像int
这样的原始(内置)类型,那么编译器可能足够智能,因此无关紧要(或不重要)。但在任何情况下,性能影响都会很小,因为调用函数的成本远高于if
的成本,因此如果您尝试测量它,差异可能会在噪声中丢失。 / p>
然而,语义却截然不同。
当我读到第一个案例时:
if (...) {
// Branch 1
} else if (...) {
// Branch 2
}
然后我知道无论两个分支可能做什么,只能执行一个。
然而,当我读到第二个案例时:
if (...) {
}
if (...) {
}
然后我不得不怀疑是否有可能两个分支被采用,这意味着我必须仔细检查第一个代码以确定它是否可能影响第二个测试。当我最终得出结论并非如此时,我诅咒这个血淋淋的开发人员,他懒得写那该死的else
,这会让我在最后10分钟的审查中得救。
因此,帮助自己和未来的维护者,并专注于使语义正确和清晰。
在这个问题上,人们可能会争辩说,这个调度逻辑可能更好地用其他结构表达,例如switch
或者map<int, void()>
? (注意后者并避免过度工程;))
答案 6 :(得分:0)
主要区别在于if / else构造将在其中一个返回true后停止评估ifs()。这意味着它可能只在执行bailing之前执行1或2个ifs。另一个版本将检查所有3个ifs,无论其他的结果如何。
所以....如果/ else的运营成本为“最多3次检查”。 if / if / if版本的运营成本为“始终进行3次检查”。假设检查所有3个值的可能性相同,if / else版本将执行平均1.5 ifs,而if / if版本将始终执行3 ifs。从长远来看,使用“else”结构可以节省1.5%的CPU时间。