而(1)比。 for(;;)有速度差吗?

长版…

while (1)在Perl脚本中使用while (1) for (;;)的速度更快,这for (;;)一名同事今天声称的。 我认为他们应该也是希望翻译能够优化任何差异。 我build立了一个循环迭代运行10亿次的脚本,以及相同数量的while循环,并logging它们之间的时间。 我无法find可观的区别。 我的同事说,一位教授告诉他, while (1)正在做一个比较1 == 1for (;;)不是。 我们使用C ++重复了100次的迭代次数,差异可以忽略不计。 然而,这是一个graphics化的例子,说明编译代码与脚本语言相比有多快。

短版…

如果你需要一个无限循环来打破,那么有没有什么理由更喜欢while (1)一个for (;;)

注意:如果问题不清楚。 这纯粹是几个朋友之间有趣的学术讨论。 我意识到这不是一个所有程序员都应该为之苦恼的超级重要的概念。 感谢所有伟大的答案(我确信其他人)从这个讨论中学到了一些东西。

更新:上面提到的同事在下面回答了一个答案。

引用这里,以防被埋葬。

它来自AMD程序员。 他表示,C程序员(人员)没有意识到他们的代码效率低下。 他今天说,海湾合作委员会的编译器是非常好的,并把像他这样的人停业。 他举例说,并且告诉了我关于while 1 vs for(;;) 。 我现在习惯使用它,但gcc,特别是解释器,现在都做同样的操作(处理器跳转),因为它们被优化了。

在Perl中,它们导致相同的操作码:

 $ perl -MO=Concise -e 'for(;;) { print "foo\n" }' a <@> leave[1 ref] vKP/REFC ->(end) 1 <0> enter ->2 2 <;> nextstate(main 2 -e:1) v ->3 9 <2> leaveloop vK/2 ->a 3 <{> enterloop(next->8 last->9 redo->4) v ->4 - <@> lineseq vK ->9 4 <;> nextstate(main 1 -e:1) v ->5 7 <@> print vK ->8 5 <0> pushmark s ->6 6 <$> const[PV "foo\n"] s ->7 8 <0> unstack v ->4 -e syntax OK $ perl -MO=Concise -e 'while(1) { print "foo\n" }' a <@> leave[1 ref] vKP/REFC ->(end) 1 <0> enter ->2 2 <;> nextstate(main 2 -e:1) v ->3 9 <2> leaveloop vK/2 ->a 3 <{> enterloop(next->8 last->9 redo->4) v ->4 - <@> lineseq vK ->9 4 <;> nextstate(main 1 -e:1) v ->5 7 <@> print vK ->8 5 <0> pushmark s ->6 6 <$> const[PV "foo\n"] s ->7 8 <0> unstack v ->4 -e syntax OK 

同样在GCC:

 #include <stdio.h> void t_while() { while(1) printf("foo\n"); } void t_for() { for(;;) printf("foo\n"); } .file "test.c" .section .rodata .LC0: .string "foo" .text .globl t_while .type t_while, @function t_while: .LFB2: pushq %rbp .LCFI0: movq %rsp, %rbp .LCFI1: .L2: movl $.LC0, %edi call puts jmp .L2 .LFE2: .size t_while, .-t_while .globl t_for .type t_for, @function t_for: .LFB3: pushq %rbp .LCFI2: movq %rsp, %rbp .LCFI3: .L5: movl $.LC0, %edi call puts jmp .L5 .LFE3: .size t_for, .-t_for .section .eh_frame,"a",@progbits .Lframe1: .long .LECIE1-.LSCIE1 .LSCIE1: .long 0x0 .byte 0x1 .string "zR" .uleb128 0x1 .sleb128 -8 .byte 0x10 .uleb128 0x1 .byte 0x3 .byte 0xc .uleb128 0x7 .uleb128 0x8 .byte 0x90 .uleb128 0x1 .align 8 .LECIE1: .LSFDE1: .long .LEFDE1-.LASFDE1 .LASFDE1: .long .LASFDE1-.Lframe1 .long .LFB2 .long .LFE2-.LFB2 .uleb128 0x0 .byte 0x4 .long .LCFI0-.LFB2 .byte 0xe .uleb128 0x10 .byte 0x86 .uleb128 0x2 .byte 0x4 .long .LCFI1-.LCFI0 .byte 0xd .uleb128 0x6 .align 8 .LEFDE1: .LSFDE3: .long .LEFDE3-.LASFDE3 .LASFDE3: .long .LASFDE3-.Lframe1 .long .LFB3 .long .LFE3-.LFB3 .uleb128 0x0 .byte 0x4 .long .LCFI2-.LFB3 .byte 0xe .uleb128 0x10 .byte 0x86 .uleb128 0x2 .byte 0x4 .long .LCFI3-.LCFI2 .byte 0xd .uleb128 0x6 .align 8 .LEFDE3: .ident "GCC: (Ubuntu 4.3.3-5ubuntu4) 4.3.3" .section .note.GNU-stack,"",@progbits 

所以我想答案是,在许多编译器中它们是一样的。 当然,对于一些其他的编译器来说,这可能不一定是这样,但是循环内的代码几乎比循环本身贵几千倍,所以谁在乎呢?

使用GCC,它们似乎都编译成相同的汇编语言:

 L2: jmp L2 

没有太多理由select一个。 我认为while(1) ,特别是while(true)for(;;)更具可读性,但这只是我的偏好。

根据标准没有区别。 6.5.3 / 1有:

for语句

 for ( for-init-statement ; conditionopt ; expressionopt ) statement 

相当于

 { for-init-statement while ( condition ) { statement expression ; } } 

而6.5.3 / 2有:

条件和expression中的任何一个或两个都可以省略。 缺less的条件使隐含的while子句等于while(true)。

所以根据C ++标准的代码:

 for (;;); 

与以下内容完全相同:

 { while (true) { ; ; } } 

用于发出警告的Visual C ++编译器

 while (1) 

(不断的expression),但不是

 for (;;) 

由于这个原因,我一直坚持倾向于for (;;)的做法,但是我不知道编译器是否仍然这样做。

for(;;)来说,如果你想朝这个方向去优化事物,那么就less了一个字符。

Turbo C与这个旧编译器for(;;)结果是代码更快,然后while(1)

今天gcc,Visual C(我认为几乎所有的)编译器都能很好地进行优化,4.7 MHz的CPU很less被使用。

在那些日子里, for( i=10; i; i-- )for( i=1; i <=10; i++ )快,因为比较i是0,导致CPU-Zero-Flag条件跳转。 零标志被修改了最后的递减操作( i-- ) ,不需要额外的cmp操作。

  call __printf_chk decl %ebx %ebx=iterator i jnz .L2 movl -4(%ebp), %ebx leave 

在这里用for(i=1; i<=10; i++)加上cmpl:

  call __printf_chk incl %ebx cmpl $11, %ebx jne .L2 movl -4(%ebp), %ebx leave 

对于所有的人争论,你不应该使用indefinte while循环,并build议像使用开放goto (严重,哎哟)

 while (1) { last if( condition1 ); code(); more_code(); last if( condition2 ); even_more_code(); } 

不能真正有效地代表任何其他方式。 不是没有创build一个退出variables,并做黑魔法保持同步。

如果你喜欢goto-esque的语法,可以使用一些能够限制范围的内容。

 flow: { if ( condition ){ redo flow; } if ( othercondition ){ redo flow; } if ( earlyexit ){ last flow; } something(); # doesn't execute when earlyexit is true } 

最终速度不是那么重要

担心如何有效的速度明智的不同循环构造是一个巨大的浪费时间。 不成熟的优化贯穿始终。 我想不出任何我所见过的分析代码在我select的循环构造中发现瓶颈的情况。

一般来说,它的循环和循环是如何的。

你应该为了可读性和简洁性而“优化”,并且写下一些最好的解释问题给下一个find你的代码的穷人。

如果您使用“goto LABEL”技巧提到某个人,而且我必须使用您的代码,请准备好单独打开睡觉,特别是如果您多次执行此操作,则会造成可怕的意大利面条代码。

只是因为你可以创build意大利面代码并不意味着你应该

从Stroustrup,TC ++ PL(第3版),§6.1.1:

for (;;)的好奇符号是指定无限循环的标准方法; 你可以发音为“永远”。 while (true)是另一种select。

我更喜欢for (;;)

如果编译器没有进行任何优化,则for(;;)总是比while(true)快。 这是因为while语句每次都会评估条件,但是for语句是无条件的跳转。 但是如果编译器优化了控制stream,它可能会产生一些操作码。 你可以很容易地阅读反汇编代码。

PS你可以写这样一个无限循环:

 #define EVER ;; //... for (EVER) { //... } 

我曾经听说过这个

它来自AMD程序员。 他指出C程序员(人们)没有意识到他们的代码效率低下。 他今天说,海湾合作委员会的编译器是非常好的,并把像他这样的人停业。 他举例说,并且告诉了我关于while 1 vs for(;;) 。 我现在习惯使用它,但gcc,特别是解释器,现在都做同样的操作(处理器跳转),因为它们被优化了。

在编译语言的优化构build中,二者之间应该没有明显的区别。 也不应该在运行时执行任何比较,他们只会执行循环代码,直到您手动退出循环(例如break )。

我感到惊讶的是没有人正确地testingfor (;;)while (1) perl!

因为perl是解释语言,所以运行perl脚本的时间不仅包括执行阶段(在本例中是相同的),还包括执行前的解释阶段。 在进行速度比较时,这两个阶段都必须考虑在内。

幸运的是,perl有一个方便的Benchmark模块 ,我们可以用它来实现一个基准,如下所示:

 #!/usr/bin/perl -w use Benchmark qw( cmpthese ); sub t_for { eval 'die; for (;;) { }'; } sub t_for2 { eval 'die; for (;;) { }'; } sub t_while { eval 'die; while (1) { }'; } cmpthese(-60, { for => \&t_for, for2 => \&t_for2, while => \&t_while }); 

请注意,我正在testing两个不同版本的无限for循环:一个比while循环短,另一个有一个额外的空间使其与while循环长度相同。

在Ubuntu 11.04 x86_64和perl 5.10.1上,我得到了以下结果:

           for2的时候
为100588 / s  -  -0%-2%
 for2 100937 / s 0% -  -1%
而102147 / s 2%1% - 

while循环显然是这个平台上的赢家。

在FreeBSD 8.2 x86_64上使用perl 5.14.1:

          for2的时候
为53453 / s  -  -0%-2%
 for2 53552 / s 0% -  -2%
而54564 / s 2%2% - 

while循环也是这里的赢家。

在FreeBSD 8.2 i386上用perl 5.14.1:

          Rate for for2
而24311 / s  -  -1%-1%
为24481 / s 1%-1%
 for2 24637 / s 1%1% - 

令人惊讶的是,带有额外空间的for循环是这里最快的select!

我的结论是,如果程序员正在为速度进行优化,则应该在x86_64平台上使用while循环。 显然在优化空间时应该使用for循环。 我的结果不幸的是在其他平台。

理论上,一个完全天真的编译器可以在二进制文件(浪费空间)中存储文字“1”,并检查每次迭代是否为1 == 0(浪费时间和更多空间)。

然而,实际上,即使“不”优化,编译器仍然会同时减less。 他们也可能会发出警告,因为它可能表明一个逻辑错误。 例如, while的论点可以在其他地方定义,你不知道它是不变的。

我很惊讶没有人提供了更直接的forms,对应于所需的大会:

 forever: do stuff; goto forever; 

while(1)for(;;)一个成语,被大多数编译器认可。

我很高兴看到perl也认识until(0)

总结for (;;) vs while (1)辩论,很明显前者在较旧的非优化编译器时代速度更快,这就是为什么你倾向于在较老的代码库中看到它,例如Lions Unix源代码评论,然而在优化编译器的坏蛋时代,这些收益被优化,与后者比前者更容易理解的事实相结合,我相信这是更可取的。

我认为两者在performance上是一致的。 但我宁愿(1)为了可读性,但我质疑为什么你需要一个无限循环。

他们是一样的。 还有更重要的问题需要思考。