而(1)比。 for(;;)有速度差吗?
长版…
while (1)
在Perl脚本中使用while (1)
for (;;)
的速度更快,这for (;;)
一名同事今天声称的。 我认为他们应该也是希望翻译能够优化任何差异。 我build立了一个循环迭代运行10亿次的脚本,以及相同数量的while循环,并logging它们之间的时间。 我无法find可观的区别。 我的同事说,一位教授告诉他, while (1)
正在做一个比较1 == 1
, for (;;)
不是。 我们使用C ++重复了100次的迭代次数,差异可以忽略不计。 然而,这是一个graphics化的例子,说明编译代码与脚本语言相比有多快。
短版…
如果你需要一个无限循环来打破,那么有没有什么理由更喜欢while (1)
一个for (;;)
?
注意:如果问题不清楚。 这纯粹是几个朋友之间有趣的学术讨论。 我意识到这不是一个所有程序员都应该为之苦恼的超级重要的概念。 感谢所有伟大的答案(我确信其他人)从这个讨论中学到了一些东西。
更新:上面提到的同事在下面回答了一个答案。
引用这里,以防被埋葬。
它来自AMD程序员。 他表示,C程序员(人员)没有意识到他们的代码效率低下。 他今天说,海湾合作委员会的编译器是非常好的,并把像他这样的人停业。 他举例说,并且告诉了我关于
while 1
vsfor(;;)
。 我现在习惯使用它,但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)为了可读性,但我质疑为什么你需要一个无限循环。
他们是一样的。 还有更重要的问题需要思考。