哪个更快:while(1)或while(2)?


587

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

哪个更快?

while(1) {
    // Some code
}

要么

while(2) {
    //Some code
}

我说这两者具有相同的执行速度,因为内部表达式while最终应求值为trueor false。在这种情况下,两个true条件都求和,并且条件内部没有额外的条件指令while。因此,两者将具有相同的执行速度,我更喜欢while(1)。

但是面试官充满信心地说:“检查您的基础知识。while(1)比更快while(2)。” (他没有测试我的信心)

这是真的?

另请参见:“ for(;;)”是否比“ while(TRUE)”更快?如果没有,人们为什么要使用它?


202
像样的编译器会将两种形式的优化都优化为零。

64
在优化的构建中,每一个while(n)都会将n!= 0或for(;;)转换为Assembly循环,循环的开头是标签,最后是goto。完全相同的代码,相同的性能。
Alex F

60
毫不奇怪,股票优化0x100000f90: jmp 0x100000f90两个片段带来了(地址明显不同)。面试官可能对冲了一次注册测验和一个简单的标记跳变。问题及其假设都是la脚的。
WhozCraig 2014年

50
面试官的这个问题与dilbert.com/strips/comic/1995-11-17共同主持 -您会遇到一个真正相信他们在说什么的人,而不管他们陈述中的愚蠢程度如何。只需从以下选项中进行选择即可:深造,发誓,大笑,大哭,以及上述各项的组合:)
GMasucci 2014年

3
@Mike W:人们可能想知道编译器应该做什么:转换为Halt语句,还是认为循环在无限时间后退出并优化了无限延迟?
伊夫·达乌斯特2014年

Answers:


681

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

使用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

我不能很好地阅读汇编,但这显然是一个无条件循环。该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"

第二个示例(返回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返回到先前的标签)。

TL; DR

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

这个故事的寓意是:

  • C ++源代码和CPU指令之间存在翻译层,并且这一层对性能具有重要意义。
  • 因此,不能仅通过查看源代码来评估性能。
  • 编译器应该足够聪明以优化这种琐碎的情况。在绝大多数情况下,程序员不应浪费时间思考它们。

196
也许面试官没有使用gcc
MM

106
@Matt McNabb这是一个好主意,但是如果面试官依赖于编译器特定的优化,那么他们需要在问题中对此非常明确,并且他们需要接受“没有区别”的答案是正确的。一些(大多数?)编译器。
乔纳森·哈特利

109
为了消除任何疑问,我在clang 3.4.2中对此进行了测试,并且两个循环在每个-O级别都产生相同的程序集。
Martin Tournoij 2014年

16
我并不感到意外,因为您在循环的条件部分中放置的所有内容都是编译时常量。因此,我怀疑编译器会看到它们的循环始终是对还是错,并且可以恭敬地简单地jmp回到开始或完全删除循环。
sherrellbc 2014年

10
@hippietrail我看不到循环的内容(或缺少它)如何可能会影响这些优化(除了任何break语句的可能性),但无论如何我还是测试了它,即使循环中有代码,跳转也是对于while(1)和两者都是绝对和无条件的while(2)。如果您真正关心的话,请自己亲自测试其他人。
接近

282

是的,while(1)就是速度远远超过while(2)供人阅读!如果我看到while(1)在一个陌生的代码库中的话,我会立即知道作者的意图,而我的眼球可以继续到下一行。

如果我看到的话while(2),我可能会停下脚步,试图弄清楚作者为什么不写信while(1)。作者的手指在键盘上打滑了吗?此代码库的维护者是否将while(n)晦涩的注释机制用作使循环看起来不同的注释机制?在某些损坏的静态分析工具中,是否存在针对伪造警告的粗略解决方法?还是我正在读取生成的代码的线索?这是由于错误的“查找并替换所有”,错误的合并或宇宙射线导致的错误吗?也许这行代码应该做的事情大不相同。也许应该阅读while(w)或阅读while(x2)。我最好在文件的历史记录中找到作者,然后向他们发送“ WTF”电子邮件...现在,我已经打破了我的思维范围。while(2)while(1) 会花一秒钟的时间!

我很夸张,但只有一点点。代码的可读性非常重要。值得在采访中提及!


我的想法恰恰是为什么(1)更快?虽然我认为这只是经理试图说服自己知道不知道编码的老情况,但大家都看到了,每个人都想成为绝地武士
ekerner

8
绝对,这一点都不夸张。肯定在这里使用svn-annotate or或git blame(或其他任何东西),并且通常花费几分钟来加载文件归咎历史。然后才最终决定“啊,我明白了,作者刚从高中毕业时就写下了这
句话

11
投票否决,因为我厌倦了惯例。开发人员有一个微不足道的机会来写自己喜欢的数字,然后有人无聊地来聊聊为什么不喜欢1
Utkan Gezer 2014年

1
因修辞而被否决。读取while(1)的速度与while(2)完全相同。
hdante 2014年

4
当我while(2)在代码中看到代码时,我经历了与该答案中描述的完全相同的思维过程(考虑到“此代码库的维护者是否将while(n)用作模糊的注释机制来使循环看起来不同?”) )。所以不,你并不夸张!
Hisham HM

151

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

就语言定义而言,抽象机中 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。

因此相关的常数表达式是1 == 02 == 0,两者都int取值0。(这些比较在while循环的语义中是隐式的;它们不作为实际的C表达式存在。)

幼稚的编译器可能会为这两种构造生成不同的代码。例如,对于第一种方法,它可以生成无条件的无限循环(1作为特殊情况进行处理),对于第二种方法,它可以生成与等效的显式运行时比较2 != 0。但是我从未遇到过实际会以这种方式运行的C编译器,而且我严重怀疑这种编译器是否存在。

大多数编译器(我很想说所有具有生产质量的编译器)都具有请求其他优化的选项。在这种选择下,任何编译器为这两种形式生成不同代码的可能性均更低。

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

底线:while (1)while(2) 几乎肯定有同样的表现。它们具有完全相同的语义,没有充分的理由让任何编译器不生成相同的代码。

尽管它是完全合法的编译器生成速度更快的代码while(1)比对while(2),这是同样的法律对编译器生成速度更快的代码while(1)比另一个发生while(1)在相同的程序。

(在您询问的问题中还暗含了另一个问题:您如何与坚持不正确的技术观点的面试官打交道。对于Workplace网站,这可能是个好问题)。


8
“在这种情况下,相关的(隐式)常量表达式为1!= 0和2!= 0,这两个表达式的int值均为1”。这过于复杂和不准确。该标准只是说,控制表达式while必须是标量类型,并且循环主体将重复执行,直到表达式比较等于0为止。这并不是说存在一个隐式expr != 0被求值...顺便将0或1与0进行比较。不,表达式与0进行比较,但该比较不会产生值。PS我赞成。
Jim Balter 2014年

3
@JimBalter:我明白你的意思,我将更新我的答案以解决它。不过,我的意思是,标准的措辞“ ...直到控制表达式比较等于0”意味着评估<expr> == 0;这就是“比较等于0” 在C语言中的含义。该比较是while循环语义的一部分。在标准或我的回答中,没有暗示需要将结果0再次进行比较。(我应该写==而不是写!=。)但是我的答案的那部分不清楚,我将对其进行更新。
基思·汤普森

1
““等于0”在C中意味着” –但是该语言是在标准中,而不是C中……实现进行比较,它不会生成C语言比较。您写“两者的求值都为int值1”-但从未发生过此类求值。如果你看一下生成的代码while (2)while (pointer)while (9.67),用最幼稚,未优化的编译器,你将看不到收益率产生的任何代码01。“我应该写==而不是!=“ -不,那没有任何意义。
Jim Balter 2014年

2
@JimBalter:嗯。我并不是要暗示“比较等于0”表示存在... == 0C表达式。我的观点是,标准的while循环描述所需的“等于0” 和一个显式x == 0表达式在逻辑上都暗示着相同的操作。而且我认为,一个非常幼稚的C编译器可能会生成代码,该代码会生成或为任何循环生成一个int值-尽管我不相信任何实际的编译器都这么幼稚。01while
基思·汤普森

12

141

等一下。面试官,他长得像这个家伙吗?

在此处输入图片说明

面试官本人未能通过面试,这是非常糟糕的,如果该公司的其他程序员“通过”了该测试该怎么办?

号评估的声明1 == 0,并2 == 0 同样快。我们可以想象一个较差的编译器实现,其中一个可能比另一个更快。但是,没有充分的理由说明为什么一个比另一个要快。

即使在某些情况下该声明为真,也不应基于对晦涩(在这种情况下为令人毛骨悚然)琐事的知识来评估程序员。不用担心这次采访,最好的办法就是走开。

免责声明:这不是原始的Dilbert卡通。这仅仅是一个混搭


6
如果包含该问题的答案,那将很有趣。
科迪·格雷

不,但实际上,我们都可以很容易地想到,由严肃的公司编写的所有编译器都将产生合理的代码。让我们以“非优化案例” / O0为例,也许最终会像anatolyg那样发布。然后,这是CPU的问题,cmp操作数比2到0比1到0的运行周期少吗?通常执行cmp需要多少个周期?它根据位模式可变吗?它们是否更慢了“复杂”的位模式cmp?我个人不知道。您可以想象一个超级愚蠢的实现,它从0到n逐位检查(例如n = 31)。
v.oddou 2014年

5
这也是我的意思:cmp操作数对于1和200 应该同样快。可能我们可以想象不是这种情况的愚蠢的实现。但是我们能想象一个比它更快的非愚蠢的实现吗?同样,如果在某个史前时代,唯一可用的实现就是像这样的白痴,今天我们应该大惊小怪吗?我不这么认为,这是尖刻的上司讲话,那是真正的宝石!while(1)while(200)
janos

@ v.ouddou“ cmp操作数在1到0的情况下比2在0的情况下运行的周期更少” –否。您应该了解什么是周期。“我个人不知道。您可以想象一个超级愚蠢的实现,它会从0到n逐点检查。”或者反之,仍然使面试官变得毫无头绪。为什么还要担心一点一点地检查呢?该实现可能是一个框框框框的人,他决定在评估程序的过程中午休。
Jim Balter 2014年

79

您的解释是正确的。除了技术知识外,这似乎是一个测试您的自信心的问题。

顺便说一句,如果你回答

这两段代码的速度一样快,因为它们都需要花费无限的时间才能完成

面试官会说

但是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 2014年

10
icepack“ while的操作数是布尔类型” —完全是胡说八道。你是面试官吗?我建议您在声明之前先熟悉C语言及其标准。
Jim Balter 2014年

29
“我没有弥补。” -请不要在胡说八道的冰袋。C没有布尔类型(在stdbool.h中确实具有_Bool,但这并不相同,并且whilelong早于其语义),并且操作数while不是布尔或_Bool或任何其他特定类型。的操作数while可以是任何表达式... while中断为0并继续为非0。
吉姆·巴尔特

36
“这两段代码都一样快,因为它们都需要花费无限的时间才能完成”,这让我想到了一些有趣的东西。终止无限循环的唯一方法是被带电粒子翻转或硬件故障:将语句从更改while (00000001) {}while (00000000) {}。真实位越多,则值转换为false的机会越小。可悲的是,2也只有一个真位。但是,3会运行更长的时间。这也仅适用于并不总是对此进行优化的编译器(VC ++)。
乔纳森·迪金森

8
@ mr5不。为了使翻转实际上产生这样的结果,您要谈论的是数万年的执行时间。只是一个思想实验。如果您因不朽的比赛而欢呼,则可能需要使用-1来防止位翻转影响程序。
乔纳森·迪金森

60

该问题最可能的解释是,访问员认为处理器逐个检查数字的各个位,直到达到非零值为止:

1 = 00000001
2 = 00000010

如果“为零?” 算法从数字的右侧开始,必须检查每个位,直到达到非零位为止,while(1) { }循环每次迭代必须检查的位是循环的两倍while(2) { }

这需要一个关于计算机如何工作的非常错误的思维模型,但是它确实有其自己的内部逻辑。一种检查方法是询问是否while(-1) { }while(3) { }将同样快,或者甚至while(32) { }更慢


39
我假设访问者的误解更像是“ 2是一个整数,需要将其转换为布尔值才能在条件表达式中使用,而1已经是布尔值。”
罗素·波罗戈夫

7
如果比较算法从左开始,则相反。
圣保罗Ebermann

1
+1,这正是我的想法。您完全可以想象有人相信,从根本上说,cmpCPU 的算法是一个线性位校验,其中第一个差异是早期循环退出。
v.oddou 2014年

1
@PeterCordes“除了您以外,我从未听过有人认为gcc或clang的-O0输出不够精确。” 我从没说过这样的话,也根本没有想到gcc或clang。你一定是在误读我。我只是不认为MSVC产生的东西很有趣。
blubberdiblub19年

1
@PeterCordes我错了,在MSVC中的断点while(1)不会在循环之前中断,而是在表达式上中断。但是当优化表达式时,它仍然会在循环内崩溃。就拿这段代码有很多断裂。在未优化的调试模式下,您将获得此信息。优化时,许多断点会聚在一起,甚至溢出到下一个函数中(IDE在调试时会显示结果断点)- 相应的反汇编
blubberdiblub19年

32

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

他们使您陷入冲突。如果是这样,那么他们很聪明,问题也很好。对于某些行业,例如银行业,将您的问题发布到Stack Overflow可能是拒绝的原因。

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


它的确很棒,但是while(2)vs while(1)显然取自dilbert漫画。不能由任何人在他的右脑中发明它(无论如何,任何人都可能想到while(2)来写东西?)。如果您的假设是正确的,那么绝对可以给您一个如此独特的问题,以便您可以谷歌搜索。就像“ while(0xf00b442)比while(1)慢”一样,银行如何才能找到被调查者的问题呢?您是否认为他们是NSA并有权使用Keycore?
v.oddou 2014年

25

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

当时,由于“中央微型计算机”的CPU时间非常宝贵,因此程序员花费了大量时间尝试各种方法来使代码更快,更高效。就像人们编写编译器一样。我猜想他公司当时提供的唯一编译器是根据“经常遇到的可以优化的语句”进行优化的,并且在遇到while(1)并评估所有内容时采取了一些捷径其他,包括一阵子(2)。有过这样的经历可以解释他的立场和对它的信心。

录用的最佳方法可能是使高级经理带走并在“ 顺利编程的过去”上给您2-3分钟的课程,然后再顺利地引导他进入下一个面试主题。(适当的时机在这里很重要-太快了,您打断了故事-太慢了,您被标记为专注力不足的人)。在面试结束时一定要告诉他,您将非常有兴趣了解有关此主题的更多信息。


19

您应该问他他是如何得出这个结论的。在任何合适的编译器下,两者都可以编译为相同的asm指令。因此,他也应该告诉您编译器开始。即使这样,您也必须非常了解编译器和平台,才能进行理论上的猜测。最后,实际上这并不重要,因为还有其他外部因素,例如内存碎片或系统负载,对循环的影响远不止这些细节。


14
@GKFX如果您给出了答案,并且他们告诉您您错了,则没有理由您不能要求他们解释原因。如果Anatolyg是正确的,并且是对自信心的考验,那么您应该在解释为什么回答自己的方式后问他们相同。
P.Turpie

我的意思是你对他们说的第一句话。不可能是“为什么x更快?” “我不知道;为什么x更快?”。显然,回答正确后,您可以提出要求。
GKFX

18

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

我会通过提供此历史记录来回答采访者,然后说,即使我很惊讶任何编译器这样做,编译器也可能会:

  • 没有优化器通过编译器生成两个测试while(1)while(2)
  • 通过优化器传递,将指示编译器进行优化(无条件跳转),while(1)因为它们被认为是惯用的。这将需要while(2)进行测试,因此会使两者之间的性能有所不同。

我当然会向面试官补充,不要考虑while(1)while(2)并且相同的构造是低质量优化的标志,因为它们是等效的构造。


10

对这个问题的另一种看法是看您是否有勇气告诉您的经理他/她错了!以及您如何交流。

我的第一个本能是生成程序集输出,以向管理器显示任何体面的编译器都应处理它,如果不这样做,则将为此提交下一个补丁:)


8

看到这么多人深入研究这个问题,就确切地说明了为什么这很可能是一个测试,以了解您要对事物进行微优化的速度有多快。

我的答案是;没关系,我只关注我们正在解决的业务问题。毕竟,这就是我要付的钱。

而且,我while(1) {}之所以选择它是因为它更常见,而其他队友则不需要花时间去弄清楚为什么有人会选择高于1的数字。

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


1
除非获得优化某些实时代码的报酬,否则您只需要剃光1或2毫秒即可适应其运行时间要求。有人会说,这当然是优化程序的工作-也就是说,如果您的体系结构具有优化程序。
Neowizard 2014年

6

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

for (;;)

因为那没有测试。(讽刺模式)


2
这就是为什么使用它是明智的while(2),它留出了优化的空间,然后您就可以for (;;)在准备提高性能时简单地切换到。
Groo

5

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

该公司希望了解您在这种情况下的反应。由于自我怀疑或担心让面试官感到沮丧,您是否安静地坐在那里并且不力求答案是正确的?还是您愿意挑战您知道是错误的权威人士?他们想看看您是否愿意坚持自己的信念,以及是否可以以机智和尊重的方式做到这一点。


3

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

如果有人问我这个问题,我会重复Donald Knuth 1974年著名的关于过早优化的报价,如果面试官不笑不笑又走下去,他就会走。


1
考虑到他还说过:“在既定的工程学科中,容易获得的12%的改善从未被认为是微不足道的,而且我认为在软件工程中应该占据相同的观点”,我认为您是不合理的。
爱丽丝

3

也许面试官故意提出这样愚蠢的问题,并希望您提出3分:

  1. 基本推理。两个循环都是无限的,很难谈论性能。
  2. 有关优化级别的知识。他想听听您是否允许编译器为您做任何优化,这将优化条件,尤其是在该块不为空的情况下。
  3. 有关微处理器体系结构的知识。大多数体系结构都有一个特殊的CPU指令,用于与0进行比较(虽然不一定更快)。

2

这是一个问题:如果您实际上编写一个程序并测量其速度,则两个循环的速度可能会有所不同!为了进行一些合理的比较:

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

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

加上一些打印时间的代码,一些随机效果(例如循环如何在一两个缓存行中定位)可能会有所不同。一个循环完全有可能完全在一条高速缓存行中,或者在高速缓存行的开始处,或者可能跨越了两条高速缓存行。结果,面试官声称最快的实际上可能是最快的-巧合。

最坏的情况:优化的编译器无法确定循环的作用,但是可以确定执行第二个循环时产生的值与第一个循环所产生的值相同。并为第一个循环生成完整的代码,但不为第二个循环生成完整的代码。


1
“缓存行”推理仅适用于缓存极小的芯片。
Sharptooth 2014年

10
这是重点。有关速度的问题假定“其他条件都相等”。
Jim Balter 2014年

6
@JimBalter:这不是速度问题,而是关于与面试官争论的问题。进行实际测试可能证明面试官是“正确的”-出于与他的论点无关但纯粹是巧合的原因。如果您试图证明他错了,那将使您陷入尴尬的境地。
gnasher729 2014年

1
@ gnasher729除了所有逻辑上的论点之外,您所讨论的巧合情况值得研究。但是仍然盲目地相信这种情况的存在不仅是天真的,而且是愚蠢的。只要您不解释会发生什么,如何发生或为什么发生,此答案都是没有用的。
user568109 2014年

4
@ gnasher729“这不是速度问题”-我不会辩论使用不诚实修辞技巧的人。
吉姆·巴尔特

2

他们都是平等的-一样。

根据规范,任何不为0的东西都被认为是正确的,因此即使不进行任何优化,一个好的编译器也不会为while(1)或while(2)生成任何代码。编译器将为生成一个简单的检查!= 0


@djechlin-因为它们都需要1个cpu周期。
UncleKing 2014年

编译器常量将其折叠,因此甚至在运行时也不会对其进行评估。
PaulHK

2

从人们花费大量的时间和精力进行测试,证明和回答这个非常直接的问题来看,Id表示,通过提出问题,两者都变得非常缓慢。

因此,要花更多的时间...

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

从历史上讲,“ while(1)”和“ while(true)”用于构成一个无限循环,该循环希望根据肯定会发生的条件在循环内的某个阶段调用“ break”。

简单地说,“ 1”总是总为真,因此说“ while(2)”和说“ while(1 +1 == 2)”一样愚蠢,后者也将真为真。

如果您想变得完全傻,请使用:-

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

我想以为您的编码员输入的错字并不会影响代码的运行,但是如果他故意使用“ 2”只是为了很奇怪,那么在他把怪异的代码全部放入您的代码之前先将其解雇,这使他很难阅读并使用。


您进行的回滚还原了非常高代表的用户的编辑。这些人通常对政策非常了解,他们在这里向您传授这些政策。我发现您的帖子的最后一行对您的帖子没有任何帮助。即使我开了一个玩笑,我显然也很想念它,但我还是觉得这太闲话了,不值得再加上一段。
Palec 2014年

@Palec:感谢您的评论。我发现同一个人编辑了许多我的帖子,大部分只是为了删除该签名。因此,我认为他只是一个声誉巨魔,因此也就是他的声誉。在线论坛是否耗尽了语言和普通礼貌,以至于我们不再签署自己的著作?也许我是一所古老的学校,但是忽略打招呼和告别似乎很不礼貌。我们正在使用应用程序,但我们仍然是人类。
ekerner

1
Stack Exchange上不欢迎使用称呼,标语,谢谢等帮助中心也提到了同样的情况,特别是在预期行为下。论坛不是Stack Exchange的一部分,甚至不是Stack Overflow的论坛,它们都是问答网站。编辑是差异之一。此外,评论应仅用于提供帖子的反馈,而不能用于讨论(Stack Overflow Chat)。问与答与论坛有很多其他方式。有关更多信息,请访问帮助中心Meta Stack Overflow
Palec 2014年

请注意,当您拥有超过2000个代表(编辑权限)时,您将不会再从编辑中获得代表。如果不确定为什么和您不同意的修改已应用于您的帖子,或者您看到某人的不当行为,请询问Meta Stack Overflow。如果编辑者做错了事,他们可能会收到mod的通知(也许他们没有意识到),甚至会受到某种惩罚(如果行为是故意恶意的)。否则,您将获得解释为什么编辑/行为正确的说明。不必要的回滚会使修订历史混乱,并使您陷入回滚战争。仅在确实确定编辑错误时才回滚。
Palec 2014年

最后关于签字,问候等……:不包括这些手续似乎很不礼貌,但会增加信噪比,并且不会丢失任何信息。您可以选择任何想要的昵称,即您的签名。在大多数地方,您也有头像。
Palec 2014年

1

那取决于编译器。

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

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

我的意思是:这实际上不是与语言(C)相关的问题。


0

由于希望回答这个问题的人们都希望最快的循环,所以我会回答说,正如其他答案中所述,两者都被同样地编译为相同的汇编代码。不过,您可以使用“循环展开”功能向面试官提出建议一个do {} while循环,而不是while循环。

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

循环内部应有一个中断条件。

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


他为什么建议执行do {} while循环?While(1)(或while(2))做同样的事情。
Catsunami

这样做可以消除一个额外的跳跃,从而提高性能。当然,如果您知道该循环将至少执行一次
Antonin GAVREL

0

显而易见的答案是:如前所述,两个片段都将运行同样繁忙的无限循环,这会使程序无限

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

#define while(x) sleep(x);

实际上,它的while(1)速度是的两倍(或一半)while(2)


@MaxB:确实,宏技巧必须更加扭曲。我认为新版本应该可以使用,尽管C标准不能保证。
chqrlie

-4

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

  1. 该代码将循环优化为

    cmp eax, 2

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

    一个。 00000000 - 00000010 cmp eax, 2

    代替

    b。 00000000 - 00000001 cmp eax, 1

cmp仅设置标志,不设置结果。因此,在最低有效位上,我们知道是否需要借用b。而使用a则必须先进行两次减法运算,然后才能借用。


15
无论在哪种情况下,cmp都将花费1个cpu周期。
UncleKing 2014年

8
该代码将不正确。正确的代码要么负载2或1到寄存器EAX,然后用0比较EAX
gnasher729
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.