哪个更快:while(1)还是while(2)?

这是一位高级经理提出的面试问题

哪个更快

while(1){
//一些代码
}

while(2){
//一些代码
}

我说过两者的执行速度相同,因为while中的表达式应该最终计算为truefalse。在这种情况下,两者的计算结果均为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指令之间存在一层翻译,这一层对性能有重要的影响。
  • 因此,不能仅通过查看源代码来评估性能
  • 编译器应该足够聪明,能够优化这些琐碎的情况。程序员在绝大多数情况下都不应该浪费时间思考它们

发表评论