while(1) 和 while(2) 哪个更快?

Which is faster: while(1) or while(2)?

提问人:Nikole 提问时间:7/20/2014 最后编辑:CommunityNikole 更新时间:9/16/2022 访问量:97507

问:

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

哪个更快?

while(1) {
    // Some code
}

while(2) {
    //Some code
}

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

但面试官自信地说: “检查你的基础知识。 比 .” (他不是在测试我的信心)while(1)while(2)

这是真的吗?

Смотритетакже: “for(;;)” 是否比 “while (TRUE)” 快?如果不是,人们为什么要使用它?

C 性能 while-loop

评论

216赞 7/20/2014
一个半体面的编译器会将这两种形式优化到零。
72赞 Alex F 7/20/2014
在优化构建中,每隔 while(n) 时,n != 0 或 for(;;) 将被转换为 Assembly endless loop,开头是 label,结尾是 goto。完全相同的代码,相同的性能。
66赞 WhozCraig 7/20/2014
毫不奇怪,股票优化为这两个片段带来了(地址显然不同)。面试官可能对冲了注册测试与简单的标记跳跃。这个问题和他们的假设都是蹩脚的。0x100000f90: jmp 0x100000f90
54赞 GMasucci 7/22/2014
面试官的这个问题与 dilbert.com/strips/comic/1995-11-17 属于同一类——你会遇到一个真正相信他们所说的话的人,无论他们的陈述中的愚蠢程度如何。只需从以下选项中进行选择:深呼吸、咒骂、大笑、哭泣,以及上述:)的某种组合
3赞 7/23/2014
@Mike W:人们可能想知道编译器应该做什么:转换为 Halt 语句,或者考虑循环在无限时间后退出并优化无限延迟?

答:

83赞 anatolyg 7/20/2014 #1

你的解释是正确的。这似乎是一个除了技术知识之外还考验你自信心的问题。

顺便说一句,如果你回答了

这两段代码同样快,因为两者都需要无限的时间才能完成

面试官会说

但每秒可以做更多的迭代;你能解释一下为什么吗?(这是无稽之谈,再次考验你的信心)while (1)

因此,通过像你一样回答,你节省了一些时间,否则你会浪费在讨论这个糟糕的问题上。


下面是我的系统(MS Visual Studio 2012)上的编译器生成的示例代码,其中优化已关闭:

yyy:
    xor eax, eax
    cmp eax, 1     (or 2, depending on your code)
    je xxx
    jmp yyy
xxx:
    ...

启用优化后:

xxx:
    jmp xxx

因此,生成的代码是完全相同的,至少在使用优化编译器时是这样。

评论

28赞 anatolyg 7/20/2014
这段代码实际上是编译器在我的系统上输出的代码。我没有编造。
11赞 Jim Balter 7/21/2014
icepack “while 的操作数是布尔型的”——完全是胡说八道。你是面试官吗?我建议你在做出这样的声明之前先熟悉C语言及其标准。
30赞 Jim Balter 7/21/2014
“不是我编的。” ——请不要理会冰袋,谁在胡说八道。C 没有布尔类型(它在 stdbool.h 中确实有_Bool,但这并不相同,而且 long 的语义早于它),并且操作数不是布尔或_Bool或任何其他特定类型。的操作数可以是任何表达式......while 在 0 时中断,并在非 0 时继续。whilewhilewhile
38赞 Jonathan Dickinson 7/21/2014
“两段代码同样快,因为两者都需要无限的时间才能完成”让我想到了一些有趣的事情。终止无限循环的唯一方法是让带电粒子翻转一个位或硬件出现故障:将语句从 更改为 。您拥有的 true 位越多,值翻转为 false 的可能性就越小。可悲的是,2 也只有一个真正的位。但是,3 的运行时间要长得多。这也仅适用于编译器,它并不总是对此进行优化 (VC++)。while (00000001) {}while (00000000) {}
8赞 Jonathan Dickinson 7/21/2014
@mr5没有。为了让一点点翻转真正产生这样的事情,你说的是数万年来的执行时间。只是一个思想实验。如果你来自一个不朽的种族,你可能想使用 -1 来防止位翻转影响你的程序。
718赞 ApproachingDarknessFish 7/20/2014 #2

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

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

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

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

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

作为参考,以下是生成的程序集(与优化标志一起使用):gcc main.c -S -masm=intel

跟:-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

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

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

在 ,这两个示例实际上调用并执行了每次迭代的比较。-O0x

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

但是,在上述位置,它们都生成与前面的示例相同的程序集(无条件地返回到前面的标签)。-O1jmp

TL的;博士

在 GCC 下,不同的循环被编译为相同的程序集。编译器计算常量值,并且不费心执行任何实际比较。

这个故事的寓意是:

  • C 源代码和 CPU 指令之间存在一层转换,这一层对性能有重要影响。
  • 因此,不能仅通过查看源代码来评估性能。
  • 编译器应该足够聪明,可以优化这种微不足道的情况。在绝大多数情况下,程序员不应该浪费时间去思考它们。

评论

211赞 M.M 7/20/2014
也许面试官没有使用 gcc
125赞 Jonathan Hartley 7/21/2014
@Matt McNabb 这是一个很好的观点,但如果面试官依赖于特定于编译器的优化,那么他们需要在问题中非常明确地说明这一点,并且他们需要接受“没有区别”的答案,因为对于某些(大多数?)编译器来说是正确的。
119赞 Martin Tournoij 7/21/2014
为了消除任何疑问,我在 clang 3.4.2 中对此进行了测试,两个循环在每个级别都生成相同的程序集。-O
19赞 sherrellbc 7/21/2014
我一点也不觉得这令人惊讶,因为您在循环的条件部分中放置的所有内容都是编译时常量。因此,我怀疑编译器会看到它们循环始终是真或假,并且要么恭敬地简单地回到开头,要么完全删除循环。jmp
12赞 ApproachingDarknessFish 7/26/2014
@hippietrail 我看不出循环的内容(或缺乏内容)如何影响这些优化(除了任何语句的可能性),但我无论如何都只是测试了它,没有,即使循环内有代码,跳转对于 和 都是绝对和无条件的。如果您真的担心,请随时自己测试其他人。breakwhile(1)while(2)
22赞 Rad'Val 7/20/2014 #3

你应该问他是如何得出这个结论的。在任何像样的编译器下,两者编译为相同的 asm 指令。所以,他应该告诉你编译器也开始了。即便如此,你也必须非常了解编译器和平台,才能做出理论上的有根据的猜测。最后,这在实践中并不重要,因为还有其他外部因素,如内存碎片或系统负载,它们对循环的影响比这个细节更大。

评论

15赞 P.Turpie 7/22/2014
@GKFX 如果你已经给出了答案,而他们告诉你你错了,你没有理由不能要求他们解释为什么。如果阿纳托利格是正确的,这是对你自信心的考验,那么你应该解释为什么你以这种方式回答,并问他们同样的问题。
0赞 GKFX 7/24/2014
我的意思是你对他们说的第一件事。它不能是“为什么 x 更快?“我不知道;为什么 X 更快?显然,正确回答后,您可以问。
4赞 gnasher729 7/21/2014 #4

这里有一个问题:如果你真的编写一个程序并测量它的速度,两个循环的速度可能会不同!为了进行一些合理的比较:

unsigned long i = 0;
while (1) { if (++i == 1000000000) break; }

unsigned long i = 0;
while (2) { if (++i == 1000000000) break; }

通过添加一些打印时间的代码,一些随机效果(例如循环在一两行缓存中的位置)可能会有所不同。一个循环可能完全在一个缓存行内,或者在缓存行的开头,或者它可能跨越两个缓存行。因此,无论面试官声称什么最快,实际上可能都是最快的——这是巧合。

最坏的情况:优化编译器没有弄清楚循环的作用,但计算出执行第二个循环时生成的值与第一个循环生成的值相同。并为第一个循环生成完整的代码,但不为第二个循环生成完整的代码。

评论

1赞 sharptooth 7/21/2014
“缓存行”推理仅适用于具有极小缓存的芯片。
10赞 Jim Balter 7/21/2014
这是无关紧要的。关于速度的问题假设“其他条件相同”。
6赞 gnasher729 7/21/2014
@JimBalter:这不是速度问题,而是与面试官争吵的问题。进行实际测试可能会证明面试官“正确”的可能性——原因与他的论点无关,但纯属巧合。如果你试图证明他是错的,这会让你陷入尴尬的境地。
1赞 user568109 7/21/2014
@gnasher729 撇开所有逻辑论证不谈,你谈到的巧合案例值得一看。但仍然盲目地相信这样的情况存在,不仅是幼稚的,而且是愚蠢的。只要你不解释什么、如何或为什么会发生这种情况,这个答案是没有用的。
4赞 Jim Balter 7/22/2014
@gnasher729 “这不是一个速度问题”——我不会与那些使用不诚实的修辞技巧的人辩论。
156赞 Keith Thompson 7/21/2014 #5

现有的答案显示了由特定编译器为具有特定选项集的特定目标生成的代码,并不能完全回答问题 - 除非该问题是在该特定上下文中提出的(例如,“使用gcc 4.7.2 for x86_64 with default options哪个更快?”)。

就语言定义而言,在抽象机器中计算整数常量,并计算整数常量;在这两种情况下,将结果相等于零进行比较。语言标准完全没有说明这两种结构的相对性能。while (1)1while (2)2

我可以想象,一个非常幼稚的编译器可能会为这两种形式生成不同的机器代码,至少在不请求优化的情况下编译时是这样。

另一方面,C 编译器绝对必须在编译时评估一些常量表达式,当它们出现在需要常量表达式的上下文中时。例如,这个:

int n = 4;
switch (n) {
    case 2+2: break;
    case 4:   break;
}

需要诊断;惰性编译器没有将计算推迟到执行时间的选项。由于编译器必须能够在编译时计算常量表达式,因此即使不需要,它也没有充分的理由不利用该功能。2+2

C标准(N1570 6.8.5p4)说

迭代语句使名为循环的语句 重复执行,直到控制表达式比较等于 0.

所以相关的常量表达式是 和 ,两者的计算结果都为 。(这些比较隐含在循环的语义中;它们不作为实际的 C 表达式存在。1 == 02 == 0int0while

一个反常的幼稚编译器可能会为这两个构造生成不同的代码。例如,对于第一个,它可以生成一个无条件的无限循环(视为一种特殊情况),对于第二个,它可以生成一个等价于的显式运行时比较。但是我从来没有遇到过一个C编译器会以这种方式运行,我严重怀疑这样的编译器是否存在。12 != 0

大多数编译器(我很想说所有生产质量的编译器)都有请求额外优化的选项。在这样的选项下,任何编译器都不太可能为这两种形式生成不同的代码。

如果编译器为这两个构造生成不同的代码,请首先检查不同的代码序列是否实际上具有不同的性能。如果是这样,请尝试使用优化选项(如果可用)再次编译。如果它们仍然不同,请向编译器供应商提交错误报告。从不符合 C 标准的意义上讲,它(不一定)是一个错误,但几乎可以肯定这是一个应该纠正的问题。

一句话:几乎可以肯定具有相同的性能。它们具有完全相同的语义,任何编译器都没有充分的理由不生成相同的代码。while (1)while(2)

尽管编译器生成比 for 更快的代码是完全合法的,但编译器生成比同一程序中另一个出现的代码更快的代码同样是合法的。while(1)while(2)while(1)while(1)

(你问的那个问题中隐含着另一个问题:你如何与坚持不正确技术观点的面试官打交道。对于 Workplace 网站来说,这可能是一个好问题)。

评论

8赞 Jim Balter 7/21/2014
“在这种情况下,相关的(隐式)常量表达式是 1 != 0 和 2 != 0,两者的计算结果都是 int 值 1” ...这过于复杂,而且不准确。该标准简单地说,控制表达式必须是标量类型,并且重复循环体,直到表达式比较等于 0。它并没有说有一个被评估的隐式......这需要将结果 - 0 或 1 - 反过来与 0 进行比较,无穷无尽。否,将表达式与 0 进行比较,但该比较不会产生值。附言我投了赞成票。whileexpr != 0
3赞 Keith Thompson 7/21/2014
@JimBalter:我明白你的意思,我会更新我的答案来解决这个问题。不过,我的意思是,该标准的措辞是“......直到控制表达式比较等于 0“ 表示计算 ;这就是 C 语言中“比较等于 0”的意思。这种比较是循环语义的一部分。无论是在标准中还是在我的回答中,都没有暗示需要再次比较结果。(我应该写而不是。但我的答案的那部分不清楚,我会更新它。<expr> == 0while0==!=
1赞 Jim Balter 7/22/2014
“”比较等于 0“在 C 中表示”——但该语言在标准中,而不是在 C 中......实现执行比较,它不会生成 C 语言比较。你写了“两者的计算结果都为 int 值 1”——但从未发生过这样的计算。如果你看一下最幼稚、未优化的编译器为 , , 生成的代码,你不会看到任何生成 或 的代码。“我应该写==而不是!=”——不,这没有任何意义。while (2)while (pointer)while (9.67)01
2赞 Keith Thompson 7/22/2014
@JimBalter:嗯。我并不是说“比较等于 0”意味着 C 表达式的存在。我的观点是,标准对循环的描述所要求的“比较等于 0”和显式表达在逻辑上都暗示了相同的操作。我认为一个非常幼稚的 C 编译器可能会生成生成任何循环值的代码——尽管我不相信任何实际的编译器都那么幼稚。... == 0whilex == 0int01while
12赞 Joe 7/23/2014
注意:这已经是工作场所的问题:workplace.stackexchange.com/questions/4314/......
3赞 Nikolay Ivanchev 7/21/2014 #6

它们都是平等的——一样的。

根据规范,任何不是 0 的东西都被认为是真的,所以即使没有任何优化,一个好的编译器也不会生成任何代码 对于 while(1) 或 while(2)。编译器将对 生成一个简单的检查。!= 0

评论

0赞 UncleKing 7/23/2014
@djechlin - 因为它们都需要 1 个 cpu 周期。
0赞 PaulHK 8/7/2018
编译器常量会折叠它,因此在运行时甚至不会对其进行计算。
64赞 Ryan Cavanaugh 7/21/2014 #7

对这个问题最可能的解释是,面试官认为处理器会逐个检查数字的各个位,直到它达到非零值:

1 = 00000001
2 = 00000010

如果“is zero?”算法从数字的右侧开始,并且必须检查每个位,直到它达到非零位,则循环每次迭代必须检查两倍于循环的位数。while(1) { }while(2) { }

这需要一个非常错误的计算机工作方式的心智模型,但它确实有自己的内在逻辑。检查的一种方法是询问是否或将同样快,或者是否会更慢while(-1) { }while(3) { }while(32) { }

评论

41赞 Russell Borogove 7/21/2014
我以为面试官的误解更像是“2 是一个 int,需要转换为布尔值才能用于条件表达式,而 1 已经是布尔值了。
7赞 Paŭlo Ebermann 7/23/2014
如果比较算法从左边开始,则情况正好相反。
1赞 v.oddou 7/29/2014
+1,这正是我的想法。你完全可以想象有人相信,从根本上说,CPU的算法是一个线性的位检查,在第一个差分上提前循环退出。cmp
1赞 blubberdiblub 8/30/2019
@PeterCordes “我从来没有听过任何人(除了你)认为 gcc 或 clang 的输出不够字面意思。”我从来没有说过这样的话,我根本没有想过 gcc 或 clang。你一定是误读了我。我只是不认为 MSVC 产生的东西很搞笑。-O0
1赞 blubberdiblub 8/31/2019
@PeterCordes我错了,在 MSVC 中,在循环之前没有断点,而是在表达式上断开。但是在优化表达式时,它仍然会在循环内折叠。以这段代码为例,有很多休息时间。在未优化的调试模式下,你会得到这个。在优化时,许多断点会合并在一起,甚至溢出到下一个函数中(IDE 在调试时会显示结果断点)——相应的反汇编while(1)
36赞 Tõnu Samuel 7/22/2014 #8

当然,我不知道这位经理的真正意图,但我提出了一个完全不同的观点:当雇用一个新成员加入团队时,了解他对冲突情况的反应是有用的。

他们把你逼入冲突。如果这是真的,他们很聪明,问题很好。对于某些行业,例如银行业,将您的问题发布到 Stack Overflow 可能是被拒绝的原因。

但我当然不知道,我只是提出一个选择。

评论

2赞 v.oddou 7/29/2014
它确实很棒,但 while(2) vs while(1) 显然取自 dilbert 漫画。它不可能被头脑正常的人发明出来(无论如何,任何人怎么会想出 while(2) 作为可能写的东西?如果你的假设是正确的,你肯定会给出一个如此独特的问题,你可以谷歌搜索它。比如“while(0xf00b442)是否比while(1)慢”,否则银行将如何找到被询问者的问题?你认为他们是国家安全局并且可以访问keyscore吗?
314赞 Chris Culter 7/22/2014 #9

是的,比人类阅读要快得多!如果我在一个不熟悉的代码库中看到,我立即知道作者的意图,我的眼球可以继续到下一行。while(1)while(2)while(1)

如果我看到,我可能会停下脚步,试图弄清楚作者为什么不写。作者的手指在键盘上滑了吗?这个代码库的维护者是否使用一种晦涩难懂的注释机制来使循环看起来不同?这是某些损坏的静态分析工具中虚假警告的粗略解决方法吗?或者这是我正在阅读生成的代码的线索?这是由于不明智的查找和替换所有错误导致的错误,还是错误的合并,还是宇宙射线?也许这行代码应该做一些截然不同的事情。也许它应该阅读或.我最好在文件的历史记录中找到作者,然后向他们发送一封“WTF”电子邮件......现在我已经打破了我的心理环境。这可能会占用我几分钟的时间,而这本来只需要几分之一秒!while(2)while(1)while(n)while(w)while(x2)while(2)while(1)

我夸大其词,但只有一点点。代码的可读性非常重要。这在采访中值得一提!

评论

8赞 v.oddou 7/29/2014
绝对,这一点也不夸张。这里肯定会使用 or(或其他什么),一般来说,加载文件归咎历史记录需要几分钟时间。然后才好好决定“啊,我明白了,作者这句台词是刚从高中毕业的时候写的”,就亏了10分钟......svn-annotate git blame
15赞 Utkan Gezer 8/11/2014
投了反对票,因为我厌倦了惯例。开发人员有这个微不足道的机会来写一个他喜欢的数字,然后有人无聊地来唠叨为什么不是。1
5赞 hdante 11/29/2014
由于修辞而被否决。读取 while(1) 的速度与 while(2) 完全相同。
1赞 chqrlie 12/12/2016
while(l)看起来很像......即使是看似无辜的语句也可以隐藏子图块错误。while(1)
6赞 Hisham H M 1/22/2017
当我在代码中看到时,我经历了与一分钟前这个答案中描述的完全相同的心理过程(包括考虑“这个代码库的维护者是否使用 while(n) 作为一种晦涩的注释机制来使循环看起来不同?所以不,你没有夸大其词!while(2)
-4赞 hdost 7/23/2014 #10

我能想到为什么会更慢的唯一原因是:while(2)

  1. 该代码将循环优化为

    cmp eax, 2

  2. 当减法发生时,你实际上是在减法

    一个。00000000 - 00000010 cmp eax, 2

    而不是

    b.00000000 - 00000001 cmp eax, 1

cmp只设置标志,不设置结果。因此,在最低有效位上,我们知道是否需要借用 b。而使用 a,您必须在借用之前执行两次减法。

评论

15赞 UncleKing 7/23/2014
无论在这两种情况下,CMP 都将占用 1 个 CPU 周期。
8赞 gnasher729 7/25/2014
该代码是不正确的。正确的代码会将 2 或 1 加载到寄存器 eax 中,然后将 eax 与 0 进行比较。
29赞 OldFrank 7/23/2014 #11

我认为线索可以在“高级经理询问”中找到。这个人显然在成为经理时停止了编程,然后他/她花了几年时间才成为高级经理。从未对编程失去兴趣,但从那时起就再也没有写过一句话。因此,他的参考不是一些答案所提到的“任何像样的编译器”,而是“这个人 20-30 年前与之合作的编译器”。

当时,程序员花费了相当多的时间尝试各种方法,使他们的代码更快、更高效,因为“中央小型计算机”的 CPU 时间非常宝贵。编写编译器的人也是如此。我猜他的公司当时提供的唯一编译器是在“经常遇到的可以优化的语句”的基础上优化的,并且在遇到一段时间(1)时采取了一些捷径,并评估了其他所有内容,包括一段时间(2)。有过这样的经历可以解释他的立场和他对它的信心。

让你被录用的最好方法可能是让高级经理得意忘形,在你顺利地引导他进入下一个面试主题之前,用 2-3 分钟的时间讲授你“编程的美好时光”。(好的时机在这里很重要——太快,你就会打断故事——太慢,你就会被贴上注意力不足的标签)。在面试结束时告诉他,你非常有兴趣了解更多关于这个话题的信息。

11赞 UncleKing 7/23/2014 #12

对这个问题的另一个看法是,看看你是否有勇气告诉你的经理他/她错了!以及你能多么温柔地传达它。

我的第一个直觉是生成汇编输出,向经理展示任何像样的编译器都应该处理它,如果它不这样做,您将为它提交下一个补丁:)

2赞 user143522 7/23/2014 #13

这取决于编译器。

如果它优化代码,或者如果它对特定指令集使用相同数量的指令将 1 和 2 计算为 true,则执行速度将相同。

在实际情况下,它总是同样快,但是可以想象一个特定的编译器和一个特定的系统,当它被不同的评估时。

我的意思是:这不是一个与语言(C)相关的问题。

9赞 Bart Verkoeijen 7/23/2014 #14

看到这么多人深入研究这个问题,恰恰说明了为什么这很可能是一个测试,看看你想以多快的速度对事物进行微优化

我的答案是;这并不重要,我更愿意专注于我们正在解决的业务问题。毕竟,这就是我要得到的报酬。

此外,我会选择,因为它更常见,其他队友不需要花时间弄清楚为什么有人会选择比 1 更高的数字。while(1) {}

现在去写一些代码。;-)

评论

1赞 Neowizard 7/24/2014
除非您因优化某些实时代码而获得报酬,否则您只需要缩短 1 或 2 毫秒即可满足其运行时间需求。当然,这是优化器的工作,有人会说 - 也就是说,如果你有一个针对你的架构的优化器。
20赞 ouah 7/23/2014 #15

为了回答这个问题,我应该补充一点,我记得 C 委员会的 Doug Gwyn 写道,一些没有优化器通过的早期 C 编译器会在汇编中为 the 生成测试(相比之下,没有它)。while(1)for(;;)

我会通过给出这个历史注释来回答面试官,然后说,即使我会非常惊讶任何编译器都这样做,编译器也可以:

  • 如果没有优化器通过,编译器会生成一个测试,同时while(1)while(2)
  • 使用 optimizer pass,编译器被指示进行优化(使用无条件跳转),所有这些都是因为它们被认为是惯用的。这将留下一个测试,因此在两者之间产生性能差异。while(1)while(2)

我当然要向面试官补充一点,不考虑和相同的结构是低质量优化的标志,因为这些是等效的结构。while(1)while(2)

150赞 janos 7/25/2014 #16

等一会。面试官,他长得像这个人吗?

enter image description here

面试官自己这次面试失败已经够糟糕了, 如果这家公司的其他程序员“通过”了这个测试怎么办?

不。评估语句和应该同样快。我们可以想象糟糕的编译器实现,其中一个可能比另一个更快。但是没有充分的理由说明为什么一个应该比另一个更快。1 == 02 == 0

即使有一些晦涩难懂的情况,当声明是正确的,程序员也不应该根据对晦涩(在这种情况下是令人毛骨悚然的)琐事的了解来评估。不要担心这次采访,这里最好的举动就是走开。

免责声明:这不是原创的 Dilbert 卡通片。这只是一个混搭

评论

0赞 v.oddou 7/29/2014
不,但实际上,我们都可以很容易地想象,所有由严肃的公司编写的编译器都会产生合理的代码。让我们以“非优化案例”/O0 为例,也许它最终会像 Anatolyg 发布的那样。然后是 CPU 的问题,操作数的运行周期会比 1 到 0 比 2 到 0 的周期少吗?一般执行CMP需要多少个周期?它是否根据位模式可变?它们是否更“复杂”的位模式会减慢速度?我个人不知道。你可以想象一个超级愚蠢的实现,从秩 0 到 n 一点一点地检查(例如 n=31)。cmpcmp
5赞 janos 7/29/2014
这也是我的观点:对于 1 和 200,操作数应该同样快。也许我们可以想象愚蠢的实现,但事实并非如此。但是我们能想象一个非愚蠢的实现比?同样,如果在某个史前时代,唯一可用的实现是那样愚蠢的,那么我们今天应该大惊小怪吗?我不这么认为,这是尖头发的老板谈话,而且是真正的宝石!cmpwhile(1)while(200)
0赞 Jim Balter 9/18/2014
@v.ouddou “将 1 到 0 比 2 到 0 的周期更短的周期运行吗” -- 否。你应该了解什么是循环。“我个人不知道。你可以想象一个超级愚蠢的实现,从等级 0 到 N 一点一点地检查“——或者相反,仍然让面试官成为一个无知的白痴。为什么要担心一点一点地检查?实现可能是一个盒子里的人,他决定在评估你的程序的过程中午休一下。
0赞 Joshua 9/26/2020
将 0 或 1 加载到寄存器中可能比加载 2 更快,具体取决于 CPU 架构。但是问这个问题的人显然没有意识到循环中的测试编译得一无所有。
3赞 ekerner 7/25/2014 #17

从人们花费的时间和精力来测试、证明和回答这个非常直接的问题来看,我想说,通过提出这个问题,两者都变得非常缓慢。

所以花更多的时间在上面......

while (2)是荒谬的,因为,

while (1),历史上用于制造无限循环,该无限循环期望在循环内的某个阶段根据肯定会发生的条件被调用。while (true)break

只是在那里总是评估为真,因此,说和说它也会评估为真一样愚蠢。1while (2)while (1 + 1 == 2)

如果你想完全傻,只需使用:-

while (1 + 5 - 2 - (1 * 3) == 0.5 - 4 + ((9 * 2) / 4.0)) {
    if (succeed())
        break;
}

我认为面试官打错了一个错字,这并没有影响代码的运行,但是如果他故意使用“只是为了奇怪”,那么在他把奇怪的陈述全部放在你的代码中之前,就解雇他,使其难以阅读和使用。2

评论

0赞 Palec 7/29/2014
您所做的回滚将恢复由非常高代表的用户编辑。这些人通常非常了解这些政策,他们在这里教你。我发现你帖子的最后一行对你的帖子没有任何用处。即使我得到了我显然错过的笑话,我也会认为它太健谈了,不值得多一段。
0赞 ekerner 7/29/2014
@Palec:谢谢你的评论。我发现同一个人编辑了我的许多帖子,大多只是为了删除签名。所以我认为他只是一个名声巨魔,因此他的名声。在线论坛是否耗尽了语言和共同的礼貌,以至于我们不再签署我们的著作?也许我有点老派,但省略问候和再见似乎很不礼貌。我们正在使用应用程序,但我们仍然是人类。
1赞 Palec 7/29/2014
Stack Exchange 上,不欢迎称呼、标语、感谢等。在帮助中心中也提到了这一点,特别是在预期行为下。Stack Exchange 的任何部分,甚至 Stack Overflow 都不是论坛,它们是问答网站。编辑是区别之一。此外,评论应该仅用于提供对帖子的反馈,而不是用于讨论(Stack Overflow Chat)。Q&A 与论坛还有许多其他不同之处。更多信息请见帮助中心Meta Stack Overflow
0赞 Palec 7/29/2014
请注意,当您拥有超过 2000 次代表(编辑权限)时,您不会再从编辑中获得代表。如果对帖子应用了您不同意的原因和编辑有疑问,或者您看到某人的不当行为,请在 Meta Stack Overflow 上提问。如果编辑做错了事情,他们可能会被模组通知(也许他们没有意识到),甚至以某种方式受到惩罚(如果行为是故意恶意的)。否则,您会得到一些指针,以解释为什么编辑/行为是正确的。不必要的回滚会使修订历史记录混乱,并可能使您陷入回滚战争。只有当真正确定编辑不正确时,我才会回滚。
0赞 Palec 7/29/2014
最后,关于签名、问候......:不包括这些手续可能看起来很不礼貌,但它增加了信噪比,并且不会丢失任何信息。你可以选择任何你想要的昵称,那是你的签名。在大多数地方,你也有你的头像。
4赞 Peter Wooster 7/27/2014 #18

我曾经对 C 和汇编代码进行编程,当时这种废话可能会有所作为。当它确实有所作为时,我们将其编写在 Assembly 中。

如果有人问我这个问题,我会重复唐纳德·克努斯(Donald Knuth)1974年关于过早优化的名言,如果面试官不笑并继续前进,我就会走开。

评论

1赞 Alice 8/10/2014
鉴于他还说过“在已建立的工程学科中,12%的改进,很容易获得,永远不会被认为是边缘的,我相信同样的观点应该在软件工程中占上风”,我认为你走路是没有道理的。
6赞 pacoverflow 7/28/2014 #19

在我看来,这是被掩盖为技术问题的行为面试问题之一。有些公司会这样做——他们会问一个技术问题,对于任何有能力的程序员来说都应该很容易回答,但是当受访者给出正确答案时,面试官会告诉他们他们错了。

该公司想看看你在这种情况下会如何反应。你是否安静地坐在那里,不强迫你的答案是正确的,因为自我怀疑或害怕惹恼面试官?或者你愿意挑战一个你知道是错的当权者?他们想看看你是否愿意坚持自己的信念,以及你是否能以委婉和尊重的方式做到这一点。

5赞 Rok Kralj 9/18/2014 #20

也许面试官故意提出这么愚蠢的问题,想让你提出 3 点:

  1. 基本推理。两个循环都是无限的,很难谈论性能。
  2. 了解优化级别。他想听听你的意见,如果你让编译器为你做任何优化,它会优化条件,特别是如果块不是空的。
  3. 了解微处理器架构。大多数架构都有一个特殊的 CPU 指令,用于与 0 进行比较(虽然不一定更快)。
7赞 Tom Tanner 3/31/2016 #21

如果您担心优化,则应使用

for (;;)

因为那没有测试。(愤世嫉俗模式)

0赞 Antonin GAVREL 1/27/2018 #22

由于想要回答这个问题的人想要最快的循环,我会回答说,正如其他答案中所述,两者都同样编译成相同的汇编代码。尽管如此,你还是可以使用“循环展开”向面试官提出建议;一个 do {} while 循环而不是 while 循环。

谨慎:您需要确保循环至少始终运行一次

循环内部应有断裂条件。

同样对于这种循环,我个人更喜欢使用 do {} while(42),因为除了 0 之外的任何整数都可以完成这项工作。

评论

0赞 Catsunami 8/3/2018
他为什么会建议一个do{}while循环?while(1) (或 while(2)) 做同样的事情。
0赞 Antonin GAVREL 8/24/2018
同时执行删除一个额外的跳跃,以获得更好的性能。当然,在您知道循环至少会执行一次的情况下
0赞 chqrlie 2/22/2019 #23

显而易见的答案是:正如发布的那样,两个片段将运行一个同样繁忙的无限循环,这使得程序无限

尽管从技术上讲,将 C 关键字重新定义为宏会具有未定义的行为,但这是我能想到的唯一方法,可以使任何一个代码片段都变得快速:您可以在 2 个片段上方添加以下行:

#define while(x) sleep(x);

它确实会比 .while(1)while(2)