这是一位高级经理提出的面试问题
哪个更快
while(1){
//一些代码
}
或
while(2){
//一些代码
}
我说过两者的执行速度相同,因为while中的表达式应该最终计算为true或false。在这种情况下,两者的计算结果均为true,并且while条件中没有额外的条件指令。因此,两者的执行速度相同,我更喜欢while(1)
但采访者自信地说:
&引用;检查你的基本情况while(1)比while(2)快;
(他不是在考验我的信心)
这是真的吗
另见:Is";对于(;)”而言;快于;而(对)“;?如果没有,为什么人们使用它
这两个循环都是无限的,但我们可以看到哪一个循环每次迭代需要更多的指令/资源
使用gcc,我编译了以下两个程序,以在不同的优化级别进行组装:
int main(无效){
而(1){}
返回0;
}
int main(无效){
而(2){}
返回0;
}
即使没有优化(-O0),生成的程序集对于两个程序都是相同的。因此,两个循环之间没有速度差异
以下是生成的程序集(使用带有优化标志的gcc main.c-S-masm=intel),以供参考:
使用-O0:
.file";main.c“;
.intel_语法noprefix
.def\u main。症状自评量表2。类型32。恩德夫
文本
格洛博梅因酒店
.def main。症状自评量表2。类型32。恩德夫
.seh_proc main
主要内容:
推动rbp
.seh_pushreg rbp
mov rbp,rsp
.seh_设置帧rbp,0
副区长,32
.seh_stackalloc 32
seh_尾声
呼叫主站
.L2:
jmp.L2
.seh_endproc
.ident";通用条款:(tdm64-2)4.8.1“;
使用-O1:
.file";main.c“;
.intel_语法noprefix
.def\u main。症状自评量表2。类型32。恩德夫
文本
格洛博梅因酒店
.def main。症状自评量表2。类型32。恩德夫
.seh_proc main
主要内容:
副区长,40
.seh_stackalloc 40
seh_尾声
呼叫主站
.L2:
jmp.L2
.seh_endproc
.ident";通用条款:(tdm64-2)4.8.1“;
使用-O2和-O3(相同输出):
.file";main.c“;
.intel_语法noprefix
.def\u main。症状自评量表2。类型32。恩德夫
.section.text.startup,“;x";
.P24,,15
格洛博梅因酒店
.def main。症状自评量表2。类型32。恩德夫
.seh_proc main
主要内容:
副区长,40
.seh_stackalloc 40
seh_尾声
呼叫主站
.L2:
jmp.L2
.seh_endproc
.ident";通用条款:(tdm64-2)4.8.1“;
事实上,为循环生成的程序集对于每个优化级别都是相同的:
.L2:
jmp.L2
.seh_endproc
.ident";通用条款:(tdm64-2)4.8.1“;
重要的是:
.L2:
jmp.L2
我不能很好地阅读汇编,但这显然是一个无条件循环。jmp指令无条件地将程序重置回.L2标签,甚至不将值与真值进行比较,当然会立即再次执行此操作,直到程序以某种方式结束。这直接对应于C/C++代码:
L2:
转到L2;
编辑:
有趣的是,即使没有优化,以下循环在汇编中都产生了完全相同的输出(无条件的jmp):
while(42){
而(1==1){}
而(2==2){}
而(4<;7){}
而(3==3&;4==4){
而(8-9<;0){}
而(4.3*3e4>;=2<;<;6){
而(-0.1+02){}
甚至令我惊讶的是:
#包括<;数学h>;
while(sqrt(7)){}
而(hypot(3,4)){}
使用用户定义的函数会让事情变得更有趣:
整数x(无效){
返回1;
}
而(x()){}
#包括<;数学h>;
双x(空){
返回sqrt(7);
}
而(x()){}
在-O0,这两个示例实际上调用x,并对每个迭代执行比较
第一个示例(返回1):
.L4:
打电话给x
testl%eax,%eax
jne.L4
movl$0,%eax
addq$32,%rsp
popq%rbp
ret
.seh_endproc
.ident";通用条款:(tdm64-2)4.8.1“;
第二个示例(返回sqrt(7)):
.L4:
打电话给x
xorpd%xmm1,%xmm1
ucomisd%xmm1,%xmm0
jp.L4
xorpd%xmm1,%xmm1
ucomisd%xmm1,%xmm0
jne.L4
movl$0,%eax
addq$32,%rsp
popq%rbp
ret
.seh_endproc
.ident";通用条款:(tdm64-2)4.8.1“;
但是,在-O1及以上位置,它们都生成与前面示例相同的程序集(无条件jmp返回到前面的标签)
TL;博士
在GCC下,不同的循环被编译成相同的程序集。编译器计算常量值,不需要执行任何实际的比较
这个故事的寓意是:
- 在C++源代码和CPU指令之间存在一层翻译,这一层对性能有重要的影响。
- 因此,不能仅通过查看源代码来评估性能
- 编译器应该足够聪明,能够优化这些琐碎的情况。程序员在绝大多数情况下都不应该浪费时间思考它们