哪个更快:while(1) 或 while(2)? [英] Which is faster: while(1) or while(2)?
问题描述
这是一位高级经理提出的面试问题.
This was an interview question asked by a senior manager.
哪个更快?
while(1) {
// Some code
}
或
while(2) {
//Some code
}
我说过两者具有相同的执行速度,因为 while
中的表达式最终应评估为 true
或 false
.在这种情况下,两者的计算结果都是 true
,并且 while
条件中没有额外的条件指令.因此,两者的执行速度相同,我更喜欢 while (1).
I said that both have the same execution speed, as the expression inside while
should finally evaluate to true
or false
. In this case, both evaluate to true
and there are no extra conditional instructions inside the while
condition. So, both will have the same speed of execution and I prefer while (1).
但面试官自信地说:"检查你的基础知识.while(1)
比 while(2)
快."(他不是在考验我的信心)
But the interviewer said confidently:
"Check your basics. while(1)
is faster than while(2)
."
(He was not testing my confidence)
这是真的吗?
推荐答案
两个循环都是无限的,但我们可以看出哪一个每次迭代需要更多的指令/资源.
Both loops are infinite, but we can see which one takes more instructions/resources per iteration.
使用 gcc,我编译了以下两个程序以进行不同优化级别的汇编:
Using gcc, I compiled the two following programs to assembly at varying levels of optimization:
int main(void) {
while(1) {}
return 0;
}
int main(void) {
while(2) {}
return 0;
}
即使没有优化 (-O0
),生成的程序集是相同的对于两个程序.因此,两个循环之间没有速度差异.
Even with no optimizations (-O0
), the generated assembly was identical for both programs. Therefore, there is no speed difference between the two loops.
作为参考,这里是生成的程序集(使用带有优化标志的gcc main.c -S -masm=intel
):
For reference, here is the generated assembly (using gcc main.c -S -masm=intel
with an optimization flag):
使用-O0
:
.file "main.c"
.intel_syntax noprefix
.def __main; .scl 2; .type 32; .endef
.text
.globl main
.def main; .scl 2; .type 32; .endef
.seh_proc main
main:
push rbp
.seh_pushreg rbp
mov rbp, rsp
.seh_setframe rbp, 0
sub rsp, 32
.seh_stackalloc 32
.seh_endprologue
call __main
.L2:
jmp .L2
.seh_endproc
.ident "GCC: (tdm64-2) 4.8.1"
使用-O1
:
.file "main.c"
.intel_syntax noprefix
.def __main; .scl 2; .type 32; .endef
.text
.globl main
.def main; .scl 2; .type 32; .endef
.seh_proc main
main:
sub rsp, 40
.seh_stackalloc 40
.seh_endprologue
call __main
.L2:
jmp .L2
.seh_endproc
.ident "GCC: (tdm64-2) 4.8.1"
使用 -O2
和 -O3
(相同的输出):
With -O2
and -O3
(same output):
.file "main.c"
.intel_syntax noprefix
.def __main; .scl 2; .type 32; .endef
.section .text.startup,"x"
.p2align 4,,15
.globl main
.def main; .scl 2; .type 32; .endef
.seh_proc main
main:
sub rsp, 40
.seh_stackalloc 40
.seh_endprologue
call __main
.L2:
jmp .L2
.seh_endproc
.ident "GCC: (tdm64-2) 4.8.1"
事实上,为循环生成的程序集对于每个优化级别都是相同的:
In fact, the assembly generated for the loop is identical for every level of optimization:
.L2:
jmp .L2
.seh_endproc
.ident "GCC: (tdm64-2) 4.8.1"
重要的部分是:
.L2:
jmp .L2
我不能很好地阅读汇编,但这显然是一个无条件循环.jmp
指令无条件地将程序重置回 .L2
标签,甚至没有将值与 true 进行比较,当然会立即再次这样做,直到程序以某种方式结束.这个直接对应C/C++代码:
I can't read assembly very well, but this is obviously an unconditional loop. The jmp
instruction unconditionally resets the program back to the .L2
label without even comparing a value against true, and of course immediately does so again until the program is somehow ended. This directly corresponds to the C/C++ code:
L2:
goto L2;
有趣的是,即使没有优化,以下循环在汇编中都产生了完全相同的输出(无条件的jmp
):
Interestingly enough, even with no optimizations, the following loops all produced the exact same output (unconditional jmp
) in assembly:
while(42) {}
while(1==1) {}
while(2==2) {}
while(4<7) {}
while(3==3 && 4==4) {}
while(8-9 < 0) {}
while(4.3 * 3e4 >= 2 << 6) {}
while(-0.1 + 02) {}
甚至令我惊讶的是:
#include<math.h>
while(sqrt(7)) {}
while(hypot(3,4)) {}
使用用户定义的函数让事情变得更有趣:
Things get a little more interesting with user-defined functions:
int x(void) {
return 1;
}
while(x()) {}
#include<math.h>
double x(void) {
return sqrt(7);
}
while(x()) {}
在-O0
,这两个例子实际上调用了x
并对每次迭代进行了比较.
At -O0
, these two examples actually call x
and perform a comparison for each iteration.
第一个例子(返回 1):
First example (returning 1):
.L4:
call x
testl %eax, %eax
jne .L4
movl $0, %eax
addq $32, %rsp
popq %rbp
ret
.seh_endproc
.ident "GCC: (tdm64-2) 4.8.1"
第二个例子(返回sqrt(7)
):
.L4:
call 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 "GCC: (tdm64-2) 4.8.1"
然而,在 -O1
及以上,它们都产生与前面示例相同的程序集(一个无条件的 jmp
回到前面的标签).
However, at -O1
and above, they both produce the same assembly as the previous examples (an unconditional jmp
back to the preceding label).
在 GCC 下,不同的循环被编译成相同的程序集.编译器会评估常量值,并且不会打扰执行任何实际比较.
Under GCC, the different loops are compiled to identical assembly. The compiler evaluates the constant values and doesn't bother performing any actual comparison.
这个故事的寓意是:
- C++ 源代码和 CPU 指令之间存在一层翻译,这一层对性能有重要影响.
- 因此,不能仅通过查看源代码来评估性能.
- 编译器应该足够聪明来优化这些微不足道的情况.在绝大多数情况下,程序员不应该浪费时间考虑它们.
- There exists a layer of translation between C++ source code and CPU instructions, and this layer has important implications for performance.
- Therefore, performance cannot be evaluated by only looking at source code.
- The compiler should be smart enough to optimize such trivial cases. Programmers should not waste their time thinking about them in the vast majority of cases.
这篇关于哪个更快:while(1) 或 while(2)?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!