长版......
一位同事在看到我在while (1)
更快的Perl脚本中使用for (;;)
之后断言了。我认为他们应该是一样的,希望翻译能够优化任何差异。我设置了一个脚本,它将运行1,000,000,000次循环迭代和相同数量的while循环并记录它们之间的时间。我找不到明显的区别。我的同事说,一位教授告诉他,while (1)
正在进行比较1 == 1
而for (;;)
没有。我们用100倍的C ++迭代次数重复相同的测试,差异可以忽略不计。然而,它是一个图形示例,说明编译代码与脚本语言相比可以更快。
短版......
如果你需要一个无限循环来突破,是否有理由更喜欢while (1)
而不是for (;;)
?
注意:如果问题不清楚。这在几个朋友之间纯粹是一次有趣的学术讨论。我知道这不是一个超级重要的概念,所有程序员都应该为之痛苦。感谢所有伟大的答案我(我相信其他人)从这次讨论中学到了一些东西。
更新:前面提到的同事在下面做出了回应。
这里引用它以防被埋葬。
它来自AMD汇编程序员。他说C程序员 (人们)没有意识到他们的代码效率低下。他说 今天虽然,gcc编译器非常好,并且让人们喜欢他 的业务。他举例说,并告诉我
while 1
vsfor(;;)
。我现在出于习惯而使用gcc,特别是口译员 这几天会做同样的操作(处理器跳转), 因为它们已经过优化。
答案 0 :(得分:214)
在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
同样在海湾合作委员会中:
#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
所以我猜答案是,它们在许多编译器中都是一样的。当然,对于其他一些编译器,情况可能不一定如此,但是循环内部的代码可能比循环本身要贵几千倍,所以谁在乎呢?
答案 1 :(得分:55)
使用GCC,它们似乎都编译为相同的汇编语言:
L2:
jmp L2
答案 2 :(得分:53)
没有太多理由偏爱一个而不是另一个。我认为while(1)
,特别是while(true)
比for(;;)
更具可读性,但这只是我的偏好。
答案 3 :(得分:31)
根据标准没有区别。 6.5.3 / 1有:
for语句
for ( for-init-statement ; conditionopt ; expressionopt ) statement
相当于
{
for-init-statement
while ( condition ) {
statement
expression ;
}
}
6.5.3 / 2有:
可以省略条件和表达式中的任何一个或两个。缺失条件使隐含的while子句等同于while(true)。
所以根据C ++标准代码:
for (;;);
与:
完全相同{
while (true) {
;
;
}
}
答案 4 :(得分:28)
用于发出
警告的Visual C ++编译器while (1)
(常量表达式)但不适用于
for (;;)
由于这个原因,我继续选择for (;;)
,但我不知道这些天编译器是否仍然这样做。
答案 5 :(得分:26)
for(;;)
可以少输入一个字符。
答案 6 :(得分:20)
使用此旧编译器for(;;)
的Turbo C会产生更快的代码while(1)
。
今天gcc,Visual C(我认为几乎所有)编译器都很好地优化了,很少使用4.7 MHz的CPU。
在那些日子里,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
答案 7 :(得分:13)
对于所有人争论你不应该使用indefinte while循环,并建议使用开放 goto 的愚蠢的东西(严重的,哎呀)
while (1) {
last if( condition1 );
code();
more_code();
last if( condition2 );
even_more_code();
}
无法以任何其他方式真正有效地表达。不是没有创建退出变量并且做黑魔法以保持同步。
如果你喜欢更多goto-esque语法,请使用限制范围的理智。
flow: {
if ( condition ){
redo flow;
}
if ( othercondition ){
redo flow;
}
if ( earlyexit ){
last flow;
}
something(); # doesn't execute when earlyexit is true
}
最终速度并不重要
考虑到速度明智的不同循环结构是多么有效浪费时间。过早优化通过。我想不出任何我见过的情况,在我选择的循环结构中,分析代码找到了瓶颈。
一般来说,循环的如何和循环的是什么。
您应该“优化”可读性和简洁性,并将最好的解释问题的内容写入找到您的代码的下一个可怜的傻瓜。
如果你使用某人提到的“goto LABEL”技巧,我必须使用你的代码,准备一只眼睛打开睡觉,特别是如果你不止一次这样做,因为那种东西会产生可怕的意大利面条代码。
仅仅因为你可以创建意大利面条代码并不意味着你应该
答案 8 :(得分:9)
来自Stroustrup,TC ++ PL(第3版),§6.1.1:
好奇的符号
for (;;)
是指定无限循环的标准方法;你可以发音“永远”。 [...]while (true)
是另一种选择。
我更喜欢for (;;)
。
答案 9 :(得分:9)
如果编译器没有进行任何优化,for(;;)
总是比while(true)
快。这是因为while语句每次都会计算条件,但for-statement是无条件跳转。但是如果编译器优化控制流,它可能会生成一些操作码。您可以非常轻松地阅读反汇编代码。
P.S。你可以像这样写一个无限循环:
#define EVER ;;
//...
for (EVER) {
//...
}
答案 10 :(得分:8)
我曾经听过这个。
它来自AMD汇编程序员。他表示,C程序员(人员)没有意识到他们的代码效率低下。他今天说,gcc编译器非常好,让像他这样的人破产。他举例说,并告诉我while 1
vs for(;;)
。我现在出于习惯而使用它,但是gcc,特别是解释器在这些日子里都会做同样的操作(处理器跳转),因为它们已经过优化。
答案 11 :(得分:5)
在编译语言的优化版本中,两者之间应该没有明显的差异。最后两者都不应该在运行时执行任何比较,它们只会执行循环代码,直到您手动退出循环(例如使用break
)。
答案 12 :(得分:3)
我很惊讶没有人在perl中正确测试for (;;)
与while (1)
!
因为perl是解释语言,所以运行perl脚本的时间不仅包括执行阶段(在这种情况下是相同的),还包括执行前的解释阶段。在进行速度比较时,必须考虑这两个阶段。
幸运的是,perl有一个方便的Benchmark module,我们可以用它来实现如下基准:
#!/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 });
请注意,我正在测试无限for循环的两个不同版本:一个比while循环短,另一个有一个额外的空间,使其与while循环的长度相同。
在Ubuntu 11.04 x86_64上使用perl 5.10.1我得到以下结果:
Rate for for2 while for 100588/s -- -0% -2% for2 100937/s 0% -- -1% while 102147/s 2% 1% --
while循环显然是这个平台的赢家。
在带有perl 5.14.1的FreeBSD 8.2 x86_64上:
Rate for for2 while for 53453/s -- -0% -2% for2 53552/s 0% -- -2% while 54564/s 2% 2% --
虽然循环也是胜利者。
在带有perl 5.14.1的FreeBSD 8.2 i386上:
Rate while for for2 while 24311/s -- -1% -1% for 24481/s 1% -- -1% for2 24637/s 1% 1% --
令人惊讶的是,带有额外空间的for循环是这里最快的选择!
我的结论是,如果程序员正在优化速度,那么应该在x86_64平台上使用while循环。显然,在优化空间时应使用for循环。遗憾的是,对于其他平台,我的结果尚无定论。
答案 13 :(得分:2)
总结for (;;)
vs while (1)
辩论很明显,前者在较旧的非优化编译器时代更快,这就是为什么你倾向于在较旧的代码库中看到它,例如狮子会的Unix源代码评论,然而在badass优化编译器的时代,那些增益被优化掉了,因为后者比前者更容易理解,我相信它会更优选。
答案 14 :(得分:2)
while(1)
是for(;;)
的惯用语,被大多数编译器识别。
我很高兴看到perl也认可until(0)
。
答案 15 :(得分:2)
理论上,完全天真的编译器可以将文字“1”存储在二进制文件中(浪费空间)并检查每次迭代是否1 == 0(浪费时间和更多空间)。
然而,实际上,即使采用“不”优化,编译器仍然会将两者都降低到相同的水平。它们也可能会发出警告,因为它可能表示存在逻辑错误。例如,while
的参数可以在其他地方定义,但你没有意识到它是恒定的。
答案 16 :(得分:2)
我很惊讶没有人提供更直接的形式,对应于所需的装配:
forever:
do stuff;
goto forever;
答案 17 :(得分:1)
只是碰到了这个问题(尽管已经晚了很多年)。
我认为我发现了“ for(;;)”比“ while(1)”更好的实际原因。
根据“ 2018年条形码编码标准”
Kernighan & Ritchie long ago recommended for (;;) , which has the additional benefit
of insuring against the visually-confusing defect of a while (l); referencing a variable ‘l’.
基本上,这不是速度问题,而是可读性问题。视代码的字体/打印而定,一段时间内的数字one(1)可能看起来像小写字母l。
即1对1。 (在某些字体中,它们看起来相同)。
因此,while(1)看起来像一些while循环,取决于变量字母L。
while(true)也可能有效,但是在某些较旧的C和嵌入式C情况下,除非包含stdbool.h,否则尚未定义true / false。
答案 18 :(得分:-3)
我认为两者在性能方面都是一样的。但我更喜欢(1)为了便于阅读,但我怀疑为什么你需要一个无限循环。
答案 19 :(得分:-14)
他们是一样的。还有更重要的问题值得思考。
<小时/> 我的观点暗示但上面没有明确说明,一个体面的编译器会为两个循环形式生成完全相同的代码。更重要的一点是,循环结构是任何算法运行时间的一小部分,您必须首先确保优化了算法以及与之相关的所有其他内容。优化循环结构应该绝对位于优先级列表的底部。