"if if"与"if else if"的性能差异
Performance difference of "if if" vs "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
)并且没有奇怪的业务(没有为int重新定义operator=),至少在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末尾的额外指令只是用于填充(使下一个函数对齐)。
这并不奇怪,在这个函数中找出p没有改变是相当基本的优化。如果p可以更改(例如,按引用传递或指针指向各种do_…
函数,或者本身是引用或指针,因此可以有别名),那么行为就不同了,当然生成的代码也会不同。
在前一种情况下,不计算匹配后的条件。
如果其他是快;如果在最后一个If语句之前找到匹配,则至少跳过最后一个If语句,如果在第一个If语句中找到匹配,则跳过所有其他语句。
if if是慢;即使使用第一个if语句找到了匹配,它也会继续尝试在其他语句中匹配。
是,性能差异是:
第二个语句求每个IF
正如已经证明的那样…它变化。
如果我们谈论的是像int
这样的原始(内置)类型,那么编译器可能足够聪明,所以它不重要(或不重要)。但是,在任何情况下,性能影响都很小,因为调用函数的成本比if
高得多,所以如果您试图测量它,那么差异可能会在噪声中丢失。
然而,语义是完全不同的。
当我读到第一个case时:
if (...) {
// Branch 1
} else if (...) {
// Branch 2
}
那么我就知道,无论这两个分支做什么,都只能执行一个。
然而,当我读到第二个例子时:
if (...) {
}
if (...) {
}
然后我不得不怀疑是否存在两个分支都被采用的可能性,这意味着我必须仔细检查第一个分支中的代码,以确定它是否可能影响第二个测试。当我最终得出结论它不是的时候,我诅咒这个该死的开发人员,他太懒了,不写该死的else
,这将节省我最后10分钟的审查。
因此,可以帮助您自己和您未来的维护者,并集中精力使语义正确和清晰。
在这个问题上,有人可能会争辩说,也许这个调度逻辑可以用其他结构来更好地表达,比如switch
或map<int, void()>
?(注意后者,避免过度设计;)
对于如此有限的表达式,您可能不会注意到性能上的任何差异。但从理论上讲,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;
}
如果…If情况可以通过在所有后续If检查中使用DONE标志来改进(因为真正的逻辑是从左到右评估的),以避免双重工作/匹配和优化。
bool bDone = false;
If( <condition> ) {
<work>;
bDone = true;
}
if (!bDone && <condition> ) {
<work>;
bDone = true;
}
或者可以使用这样的逻辑:
While(true) {
if( <condition> ) {
<work>;
break;
}
if( <condition> ) {
<work>;
break;
}
....
break;
}
虽然读起来有些混乱(问"为什么这样做")
主要区别在于,一旦其中一个if()返回true, if/else构造将停止计算if()。这意味着它在退出前可能只执行1或2个条件。另一个版本将检查所有3个if,而不管其他的结果。
所以…if/else的操作成本为"最多3次检查"。if/if/if版本的运行成本为"总是做3次检查"。假设被检查的所有3个值的可能性相等,if/else版本将平均执行1.5个if,而if/if版本总是执行3个if。从长远来看,使用"else"构造可以节省1.5 if的CPU时间。
- 删除一个线程上有数百万个字符串的大型哈希映射会影响另一个线程的性能
- OpenMP阵列性能较差
- 递归列出所有目录中的C++与Python与Ruby的性能
- if-else 与三元函数调用性能
- 性能:否则如果 vs if 在已经返回的函数中
- C++:if 内部循环的性能影响
- 从编译器优化和代码性能的角度来看,"if constexpr"与"if"
- 递归可变参数函数调用对简单 if.else 语句的性能
- 在相同条件下扩展多个"if"可提高性能
- .NET 4,C ,IF..Else和Switch()如何影响性能
- 如何在 c++ 中提高 IF 语句性能
- if/else vs虚拟功能:设计性能
- 由于if语句,C++的性能损失巨大
- "if if"与"if else if"的性能差异
- else if(cond){}和else {if(cond){}}的性能差异
- "if"语句对性能的影响有多大?
- 我是否应该使用"if"语句统一两个相似的内核,冒着性能损失的风险?
- 条件样式不断变化的 IF 性能
- 嵌套if VS . 2独立if——性能方面
- 函数指针数组优于if块的性能优势