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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/24848359/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-02 11:14:27  来源:igfitidea点击:

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

cperformancewhile-loop

提问by Nikole

This was an interview question asked by a senior manager.

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

Which is faster?

哪个更快?

while(1) {
    // Some code
}

or

或者

while(2) {
    //Some code
}

I said that both have the same execution speed, as the expression inside whileshould finally evaluate to trueor false. In this case, both evaluate to trueand there are no extra conditional instructions inside the whilecondition. So, both will have the same speed of execution and I prefer while (1).

我说过两者具有相同的执行速度,因为里面的表达式while最终应该评估为trueor false。在这种情况下,评估结果true和条件中没有额外的条件指令while。因此,两者的执行速度相同,我更喜欢 while (1)。

But the interviewer said confidently: "Check your basics. while(1)is faster than while(2)." (He was not testing my confidence)

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

Is this true?

这是真的?

See also: Is "for(;;)" faster than "while (TRUE)"? If not, why do people use it?

另请参阅:“for(;;)”比“while (TRUE)”快吗?如果不是,人们为什么要使用它?

回答by ApproachingDarknessFish

Both loops are infinite, but we can see which one takes more instructions/resources per iteration.

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

Using gcc, I compiled the two following programs to assembly at varying levels of optimization:

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

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



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

Even with no optimizations (-O0), the generated assembly was identical for both programs.Therefore, there is no speed difference between the two loops.

即使没有优化 ( -O0),两个程序生成的程序集也是相同的因此,两个回路之间没有速度差异。

For reference, here is the generated assembly (using gcc main.c -S -masm=intelwith an optimization flag):

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

With -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"

With -O1:

-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"

With -O2and -O3(same output):

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

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"

The important bits being:

重要的部分是:

.L2:
    jmp .L2

I can't read assembly very well, but this is obviously an unconditional loop. The jmpinstruction unconditionally resets the program back to the .L2label 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:

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

L2:
    goto L2;

Edit:

编辑:

Interestingly enough, even with no optimizations, the following loops all produced the exact same output (unconditional jmp) in assembly:

有趣的是,即使没有优化,以下循环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) {}

And even to my amazement:

甚至令我惊讶的是:

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

At -O0, these two examples actually call xand perform a comparison for each iteration.

在 处-O0,这两个示例实际上x对每次迭代调用并执行比较。

First example (returning 1):

第一个示例(返回 1):

.L4:
    call    x
    testl   %eax, %eax
    jne .L4
    movl    
.L4:
    call    x
    xorpd   %xmm1, %xmm1
    ucomisd %xmm1, %xmm0
    jp  .L4
    xorpd   %xmm1, %xmm1
    ucomisd %xmm1, %xmm0
    jne .L4
    movl    
int n = 4;
switch (n) {
    case 2+2: break;
    case 4:   break;
}
, %eax addq , %rsp popq %rbp ret .seh_endproc .ident "GCC: (tdm64-2) 4.8.1"
, %eax addq , %rsp popq %rbp ret .seh_endproc .ident "GCC: (tdm64-2) 4.8.1"

Second example (returning sqrt(7)):

第二个例子(返回sqrt(7)):

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

However, at -O1and above, they both produce the same assembly as the previous examples (an unconditional jmpback to the preceding label).

但是,在 at-O1和上面,它们都生成与前面示例相同的程序集(无条件jmp返回到前面的标签)。

TL;DR

TL; 博士

Under GCC, the different loops are compiled to identical assembly. The compiler evaluates the constant values and doesn't bother performing any actual comparison.

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

The moral of the story is:

这个故事的寓意是:

  • 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 besmart enough to optimize such trivial cases. Programmers should notwaste their time thinking about them in the vast majority of cases.
  • C++ 源代码和 CPU 指令之间存在一个翻译层,这一层对性能有重要影响。
  • 因此,不能仅通过查看源代码来评估性能。
  • 编译器应该足够聪明来优化这些琐碎的情况。在绝大多数情况下,程序员不应该浪费时间考虑它们。

回答by Chris Culter

Yes, while(1)is much faster than while(2), for a human to read!If I see while(1)in an unfamiliar codebase, I immediately know what the author intended, and my eyeballs can continue to the next line.

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

If I see while(2), I'll probably halt in my tracks and try to figure out why the author didn't write while(1). Did the author's finger slip on the keyboard? Do the maintainers of this codebase use while(n)as an obscure commenting mechanism to make loops look different? Is it a crude workaround for a spurious warning in some broken static analysis tool? Or is this a clue that I'm reading generated code? Is it a bug resulting from an ill-advised find-and-replace-all, or a bad merge, or a cosmic ray? Maybe this line of code is supposed to do something dramatically different. Maybe it was supposed to read while(w)or while(x2). I'd better find the author in the file's history and send them a "WTF" email... and now I've broken my mental context. The while(2)might consume several minutes of my time, when while(1)would have taken a fraction of a second!

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

I'm exaggerating, but only a little. Code readability is really important. And that's worth mentioning in an interview!

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

回答by Keith Thompson

The existing answers showing the code generated by a particular compiler for a particular target with a particular set of options do not fully answer the question -- unless the question was asked in that specific context ("Which is faster using gcc 4.7.2 for x86_64 with default options?", for example).

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

As far as the language definition is concerned, in the abstract machinewhile (1)evaluates the integer constant 1, and while (2)evaluates the integer constant 2; in both cases the result is compared for equality to zero. The language standard says absolutely nothing about the relative performance of the two constructs.

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

I can imagine that an extremely naive compiler might generate different machine code for the two forms, at least when compiled without requesting optimization.

我可以想象一个极其幼稚的编译器可能会为这两种形式生成不同的机器代码,至少在编译时没有请求优化。

On the other hand, C compilers absolutely must evaluate someconstant expressions at compile time, when they appear in contexts that require a constant expression. For example, this:

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

xxx:
    jmp xxx

requires a diagnostic; a lazy compiler does not have the option of deferring the evaluation of 2+2until execution time. Since a compiler has to have the ability to evaluate constant expressions at compile time, there's no good reason for it not to take advantage of that capability even when it's not required.

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

The C standard (N15706.8.5p4) says that

C 标准 ( N15706.8.5p4) 说

An iteration statement causes a statement called the loop bodyto be executed repeatedly until the controlling expression compares equal to 0.

迭代语句会导致重复执行称为循环体的语句,直到控制表达式比较等于 0。

So the relevant constant expressions are 1 == 0and 2 == 0, both of which evaluate to the intvalue 0. (These comparison are implicit in the semantics of the whileloop; they don't exist as actual C expressions.)

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

A perversely naive compiler couldgenerate different code for the two constructs. For example, for the first it could generate an unconditional infinite loop (treating 1as a special case), and for the second it could generate an explicit run-time comparison equivalent to 2 != 0. But I've never encountered a C compiler that would actually behave that way, and I seriously doubt that such a compiler exists.

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

Most compilers (I'm tempted to say all production-quality compilers) have options to request additional optimizations. Under such an option, it's even less likely that any compiler would generate different code for the two forms.

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

If your compiler generates different code for the two constructs, first check whether the differing code sequences actually have different performance. If they do, try compiling again with an optimization option (if available). If they still differ, submit a bug report to the compiler vendor. It's not (necessarily) a bug in the sense of a failure to conform to the C standard, but it's almost certainly a problem that should be corrected.

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

Bottom line: while (1)and while(2)almostcertainly have the same performance. They have exactly the same semantics, and there's no good reason for any compiler not to generate identical code.

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

And though it's perfectly legal for a compiler to generate faster code for while(1)than for while(2), it's equally legal for a compiler to generate faster code for while(1)than for another occurrence of while(1)in the same program.

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

(There's another question implicit in the one you asked: How do you deal with an interviewer who insists on an incorrect technical point. That would probably be a good question for the Workplace site).

(在您提出的问题中隐含了另一个问题:您如何应对坚持不正确的技术观点的面试官。这对于Workplace 网站来说可能是一个很好的问题)。

回答by janos

Wait a minute. The interviewer, did he look like this guy?

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

enter image description here

在此处输入图片说明

It's bad enough that the interviewer himself has failedthis interview, what if other programmers at this company have "passed" this test?

面试官自己这次面试不及格已经够糟糕了,万一这家公司的其他程序员都“通过”了这个测试呢?

No. Evaluating the statements 1 == 0and 2 == 0should beequally fast. We could imaginepoor compiler implementations where one might be faster than the other. But there's no goodreason why one should be faster than the other.

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

Even if there's some obscure circumstance when the claim would be true, programmers should not be evaluated based on knowledge of obscure (and in this case, creepy) trivia. Don't worry about this interview, the best move here is to walk away.

即使在某些不明确的情况下该声明是真实的,也不应该根据对晦涩(在这种情况下,是令人毛骨悚然的)琐事的知识来评估程序员。不要担心这次采访,这里最好的举动是走开。

Disclaimer:This is NOT an original Dilbert cartoon. This is merely a mashup.

免责声明:这不是原始的呆伯特卡通片。这只是一个混搭

回答by anatolyg

Your explanation is correct. This seems to be a question that tests your self-confidence in addition to technical knowledge.

你的解释是正确的。这似乎是一道考验你自信心的一道题,除了技术知识。

By the way, if you answered

顺便说一句,如果你回答

Both pieces of code are equally fast, because both take infinite time to complete

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

the interviewer would say

面试官会说

But while (1)can do more iterations per second; can you explain why? (this is nonsense; testing your confidence again)

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

So by answering like you did, you saved some time which you would otherwise waste on discussing this bad question.

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



Here is an example code generated by the compiler on my system (MS Visual Studio 2012), with optimizations turned off:

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

1 = 00000001
2 = 00000010

With optimizations turned on:

开启优化后:

##代码##

So the generated code is exactly the same, at least with an optimizing compiler.

所以生成的代码是完全一样的,至少在优化编译器的情况下是这样。

回答by Ryan Cavanaugh

The most likely explanation for the question is that the interviewer thinks that the processor checks the individual bits of the numbers, one by one, until it hits a non-zero value:

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

##代码##

If the "is zero?" algorithm starts from the right side of the number and has to check each bit until it reaches a non-zero bit, the while(1) { }loop would have to check twice as many bits per iteration as the while(2) { }loop.

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

This requires a very wrong mental model of how computers work, but it does have its own internal logic. One way to check would be to ask if while(-1) { }or while(3) { }would be equally fast, or if while(32) { }would be even slower.

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

回答by T?nu Samuel

Of course I do not know the real intentions of this manager, but I propose a completely different view: When hiring a new member into a team, it is useful to know how he reacts to conflict situations.

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

They drove you into conflict. If this is true, they are clever and the question was good. For some industries, like banking, posting your problem to Stack Overflow could be a reason for rejection.

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

But of course I do not know, I just propose one option.

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

回答by OldFrank

I think the clue is to be found in "asked by a senior manager". This person obviously stopped programming when he became a manager and then it took him/her several years to become a senior manager. Never lost interest in programming, but never wrote a line since those days. So his reference is not "any decent compiler out there" as some answers mention, but "the compiler this person worked with 20-30 years ago".

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

At that time, programmers spent a considerable percentage of their time trying out various methods for making their code faster and more efficient as CPU time of 'the central minicomputer' was so valueable. As did people writing compilers. I'm guessing that the one-and-only compiler his company made available at that time optimized on the basis of 'frequently encountered statements that can be optimized' and took a bit of a shortcut when encountering a while(1) and evaluated everything else, including a while(2). Having had such an experience could explain his position and his confidence in it.

当时,由于“中央小型机”的 CPU 时间非常宝贵,程序员花费了相当多的时间来尝试各种方法来使他们的代码更快、更高效。编写编译器的人也是如此。我猜他公司当时提供的唯一编译器是基于'经常遇到的可以优化的语句'进行优化的,遇到while(1)时走了一点捷径,并评估了一切否则,包括一段时间(2)。有过这样的经历,可以解释他的立场和他的信心。

The best approach to get you hired is probably one that enables the senior manager to get carried away and lecture you 2-3 minutes on "the good old days of programming" before YOU smoothlylead him towards the next interview subject. (Good timing is important here - too fast and you're interrupting the story - too slow and you are labelled as somebody with insufficient focus). Do tell him at the end of the interview that you'd be highly interested to learn more about this topic.

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

回答by Valentin Radu

You should have asked him how did he reached to that conclusion. Under any decent compiler out there, the two compile to the same asm instructions. So, he should have told you the compiler as well to start off. And even so, you would have to know the compiler and platform very well to even make a theoretical educated guess. And in the end, it doesn't really matter in practice, since there are other external factors like memory fragmentation or system load that will influence the loop more than this detail.

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

回答by ouah

For the sake of this question, I should that add I remember Doug Gwyn from C Committee writing that some early C compilers without the optimizer pass would generate a test in assembly for the while(1)(comparing to for(;;)which wouldn't have it).

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

I would answer to the interviewer by giving this historical note and then say that even if I would be very surprised any compiler did this, a compiler could have:

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

  • without optimizer pass the compiler generate a test for both while(1)and while(2)
  • with optimizer pass the compiler is instructed to optimize (with an unconditional jump) all while(1)because they are considered as idiomatic. This would leave the while(2)with a test and therefore makes a performance difference between the two.
  • 没有优化器通过编译器生成两个测试while(1)while(2)
  • 通过优化器传递,编译器被指示优化(无条件跳转),while(1)因为它们被认为是惯用的。这将留下while(2)一个测试,因此导致两者之间的性能差异。

I would of course add to the interviewer that not considering while(1)and while(2)the same construct is a sign of low-quality optimization as these are equivalent constructs.

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