哪个更快:while(1) 或 while(2)? [英] Which is faster: while(1) or while(2)?

查看:28
本文介绍了哪个更快:while(1) 或 while(2)?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

This was an interview question asked by a senior manager.

哪个更快?

while(1) {
    // Some code
}

while(2) {
    //Some code
}

我说过两者具有相同的执行速度,因为 while 中的表达式最终应评估为 truefalse.在这种情况下,两者的计算结果都是 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆