“if if”与“if else if”的性能差异

我只是想在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(); 

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

 0000000000000000 : 0000000000000040 : 0: 85 ff test %edi,%edi 40: 85 ff test %edi,%edi 2: 74 14 je 18  42: 74 14 je 58  4: 83 ff 01 cmp $0x1,%edi 44: 83 ff 01 cmp $0x1,%edi 7: 74 27 je 30  47: 74 27 je 70  9: 83 ff 02 cmp $0x2,%edi 49: 83 ff 02 cmp $0x2,%edi c: 74 12 je 20  4c: 74 12 je 60  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  5a: e9 00 00 00 00 jmpq 5f  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  62: e9 00 00 00 00 jmpq 67  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  72: e9 00 00 00 00 jmpq 77  37: 66 0f 1f 84 00 00 00 nopw 0x0(%rax,%rax,1) 3e: 00 00 

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

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

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

如果其他 更快 ; 如果在最后一个之前找到匹配,则至少跳过最后一个if语句,如果先找到匹配,则跳过所有其他语句。

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

是的,性能差异是:

第二个陈述评估每个IF

对于如此有限数量的表达式,您可能不会注意到性能的任何差异。 但理论上, 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; } 

正如已经certificate的那样……它各不相同。

如果我们讨论像int这样的原始(内置函数)类型,那么编译器可能足够聪明,因此无关紧要(或不重要)。 但在任何情况下,性能影响都会很小,因为调用函数的成本远高于if的成本,因此如果您尝试测量它,差异可能会在噪声中丢失。

然而,语义是完全不同的。

当我读到第一个案例时:

 if (...) { // Branch 1 } else if (...) { // Branch 2 } 

然后我知道无论两个分支可能做什么,只有一个可以执行。

但是,当我读到第二个案例时:

 if (...) { } if (...) { } 

然后我不得不怀疑是否有可能两个分支都被采用,这意味着我必须仔细检查第一个代码,以确定它是否可能影响第二个测试。 当我最终得出结论并非如此时,我诅咒这位血淋淋的开发人员,他懒得写那该死的else本来会让我在最后10分钟的审查中得救。

因此, 帮助自己和未来的维护者 ,并集中精力使语义正确和清晰。

在这个主题上,有人可能会争辩说,这个调度逻辑可能更好地用其他结构表达,比如switch或者map ? (注意后者并避免过度工程;))

主要区别在于if / else构造将在其中一个返回true时停止评估ifs()。 这意味着它只能在捞出之前执行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时间。