当前位置: 首页 > 知识库问答 >
问题:

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

夏意蕴
2023-03-14

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

哪个更快?

while(1) {
    // Some code
}
while(2) {
    //Some code
}

我说过,这两者具有相同的执行速度,因为while内部的表达式最终应计算为truefalse。在这种情况下,两者的计算结果都为truewhile条件中没有额外的条件指令。因此,两者的执行速度相同,我更喜欢while(1)。

面试官自信地说:“检查一下你的基础,while(1)while(2)要快。”(他不是在考验我的信心)

这是真的吗?

共有1个答案

柯波峻
2023-03-14

两个循环都是无限的,但是我们可以看到哪个循环每次迭代需要更多的指令/资源。

使用gcc,我编译了以下两个程序,以便在不同的优化级别上进行组装:

int main(void) {
    while(1) {}
    return 0;
}
int main(void) {
    while(2) {}
    return 0;
}

即使没有优化(-o0),生成的程序集对于两个程序都是相同的。因此,两个环路之间没有速度差异。

使用-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(相同输出):

    .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"
 .L2:
    jmp .L2
    .seh_endproc
    .ident  "GCC: (tdm64-2) 4.8.1"
.L2:
    jmp .L2

我不能很好地读取assembly,但这显然是一个无条件循环。jmp指令无条件地将程序重置回.l2标签,甚至不需要将值与true进行比较,并且当然会立即再次这样做,直到程序以某种方式结束。这直接对应于C/C++代码:

L2:
    goto L2;

编辑:

非常有趣的是,即使没有优化,以下循环都在汇编中生成了完全相同的输出(无条件jmp):

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)) {}

用户定义函数会让事情变得更有趣:

int x(void) {
    return 1;
}

while(x()) {}
#include<math.h>

double x(void) {
    return sqrt(7);
}

while(x()) {}

-o0处,这两个示例实际上调用x并为每个迭代执行一个比较。

第一个示例(返回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"
.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"

在GCC下,不同的循环被编译成相同的程序集。编译器计算常量值,而不需要执行任何实际比较。

这个故事的寓意是:

  • 在C++源代码和CPU指令之间存在一个转换层,这一层对性能有重要影响。
  • 因此,不能仅通过查看源代码来评估性能。
  • 编译器应该足够聪明来优化这种琐碎的情况。程序员在绝大多数情况下都不应该把时间浪费在考虑这些问题上。
 类似资料:
  • 问题内容: 如果您的目标是测试MySQL列中是否存在字符串(类型为’varchar’,’text’,’blob’等),那么以下哪一项是更快/更有效/更好地使用,为什么? 或者,还有其他方法可以胜任这些方法吗? 与 问题答案: 正如kibibu在上述评论中指出的,FULLTEXT搜索绝对会更快。 但是 : 在我的测试中,它们的表现完全相同。它们都不区分大小写,并且通常会执行全表扫描,这在处理高性能M

  • 1. while语句 在第 3 节 “递归”中,我们介绍了用递归求n!的方法,其实每次递归调用都在重复做同样一件事,就是把n乘到(n-1)!上然后把结果返回。虽说是重复,但每次做都稍微有一点区别(n的值不一样),这种每次都有一点区别的重复工作称为迭代(Iteration)。我们使用计算机的主要目的之一就是让它做重复迭代的工作,因为把一件工作重复做成千上万次而不出错正是计算机最擅长的,也是人类最不擅

  • 终于,来到了shell十三问的最后一问了... 长长吐一口气~~~~ 最后要介绍的是shell script设计中常见的循环(loop). 所谓的loop就是script中的一段在一定条件下反复执行的代码。 bash shell中常用的loop有如下三种: for while until 1. for loop for loop 是从一个清单列表中读进变量的值, 并依次的循环执行do到done之间

  • 我的程序中有两个while循环。第一个是针对游戏菜单的,第二个是针对实际游戏的。如果“Gameover-Event”发生,我想返回菜单。我不知道该怎么做。

  • 问题内容: 可以说我有一个图,想看看是否。哪个实施速度更快,为什么? 要么 显然,这过于简单了,但可以想象该图变得非常密集。 问题答案: 集合中的成员资格测试要快得多,尤其是对于大型集合。这是因为该集合使用哈希函数来映射到存储桶。由于Python实现会自动调整该哈希表的大小,因此无论集合的大小如何,速度都可以保持恒定()(假设哈希函数足够好)。 相反,要评估对象是否为列表的成员,Python必须比

  • 问题内容: 我知道我们可以进行相关的子查询并加入。但是哪一个更快?有黄金法则还是我必须同时衡量这两者? 问题答案: 首先,相关子查询实际上是联接的一种。关于哪一个产生最佳执行计划没有黄金法则。如果您对性能感兴趣,则需要尝试不同的表格以查看最有效的方法。或者,至少,看看执行该决定的执行计划。 通常,出于两个原因,我倾向于避免关联子查询。首先,几乎总是可以在没有相关性的情况下编写它们。其次,许多查询引