c ++ - while(1)Vs. for(;;)有速度差吗?
长版......
在看到我在Perl脚本中使用while 1之后,一位同事声称for(;;)更快。 我认为他们应该是一样的,希望翻译能够优化任何差异。 我设置了一个脚本,它将运行1,000,000,000次循环迭代和相同数量的while循环并记录它们之间的时间。 我找不到明显的区别。 我的同事说,一位教授告诉他,while (1)正在进行比较1 == 1而for (;;)则没有。 我们用100倍的C ++迭代次数重复相同的测试,差异可以忽略不计。 然而,这是一个图形示例,说明编译代码与脚本语言相比可以更快。
精简版...
如果你需要一个无限循环来突破,有没有理由比while 1更喜欢for(;;)?
注意:如果问题不清楚。 这在几个朋友之间纯粹是一次有趣的学术讨论。 我知道这不是一个超级重要的概念,所有程序员都应该为之痛苦。 感谢所有伟大的答案我(我相信其他人)从这次讨论中学到了一些东西。
更新:上述同事在下面做出回应。
这里引用以防它被埋没。
它来自AMD汇编程序员。 他说C程序员 (人们)没有意识到他们的代码效率低下。 他说 今天虽然,gcc编译器非常好,并且让人们喜欢他 的业务。 他举例说,并告诉我关于while 1与 for(;;).我现在出于习惯而使用gcc,特别是口译员 这几天会做同样的操作(处理器跳转), 因为它们是优化的。
20个解决方案
210 votes
在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
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
所以我猜答案是,它们在许多编译器中都是一样的。 当然,对于其他一些编译器,情况可能不一定如此,但是循环内部的代码可能比循环本身要贵几千倍,所以谁在乎呢?
bdonlan answered 2019-05-13T14:37:30Z
55 votes
使用GCC,它们似乎都编译为相同的汇编语言:
L2:
jmp L2
Martin Cote answered 2019-05-13T14:38:01Z
51 votes
没有太多理由偏爱一个而不是另一个。 我确实认为while(1),特别是while(true)比234580035830965950更具可读性,但这只是我的偏好。
Bill the Lizard answered 2019-05-13T14:38:35Z
31 votes
根据标准没有区别。 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) {
;
;
}
}
Richard Corden answered 2019-05-13T14:39:53Z
28 votes
用于发出警告的Visual C ++编译器
while (1)
(常数表达式)但不是
for (;;)
因为这个原因,我继续选择for (;;)的做法,但我不知道这些天编译器是否仍然这样做。
sean e answered 2019-05-13T14:40:36Z
26 votes
for(;;)如果您想朝那个方向进行优化,那么要输入的字符就少一个。
Chris Bartow answered 2019-05-13T14:41:06Z
20 votes
Turbo C与这个旧的编译器for(i=1; i<=10; i++)导致更快的代码然后for( i=1; i <=10; i++ )。
今天gcc,Visual C(我认为几乎所有)编译器都很好地优化了,很少使用4.7 MHz的CPU。
在那些日子里,for(i=1; i<=10; 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
Lutz L. answered 2019-05-13T14:41:58Z
13 votes
对于所有人争论你不应该使用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”技巧,我必须使用你的代码,准备好一只眼睛打开睡觉,特别是如果你不止一次这样做,因为那种东西会产生可怕的意大利面条代码。
仅仅因为你可以创建意大利面条代码并不意味着你应该
Kent Fredric answered 2019-05-13T14:43:36Z
8 votes
来自Stroustrup,TC ++ PL(第3版),§6.1.1:
好奇的符号for (;;)是指定无限循环的标准方法; 你可以发音“永远”。 [...] while (true)是另一种选择。
我更喜欢for (;;)。
Hans W answered 2019-05-13T14:44:23Z
8 votes
我曾经听过这个。
它来自AMD汇编程序员。 他表示,C程序员(人员)没有意识到他们的代码效率低下。 他今天说,gcc编译器非常好,让像他这样的人破产。 他举了例说,然后告诉我关于while 1和for(;;)的问题。我现在习惯使用它,但是gcc,特别是解释器在这几天都会做同样的操作(处理器跳转),因为它们已经过优化。
Jimmie Clark answered 2019-05-13T14:45:02Z
8 votes
如果编译器没有进行任何优化,则for(;;)将始终比while(true)更快。这是因为while语句每次都会计算条件,但for-statement是无条件跳转。 但是如果编译器优化控制流,它可能会生成一些操作码。 您可以非常轻松地阅读反汇编代码。
附: 你可以像这样写一个无限循环:
#define EVER ;;
//...
for (EVER) {
//...
}
silverbullettt answered 2019-05-13T14:45:43Z
5 votes
在编译语言的优化版本中,两者之间应该没有明显的差异。 两者都不应该在运行时执行任何比较,它们只会执行循环代码,直到您手动退出循环(例如,使用break)。
Charlie answered 2019-05-13T14:46:17Z
3 votes
我很惊讶没有人正确测试for (;;)与perl中的while (1)!
因为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 });
请注意,我正在测试无限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循环。 遗憾的是,我的结果对其他平台没有定论。
snap answered 2019-05-13T14:48:11Z
2 votes
从理论上讲,一个完全天真的编译器可以将文字“1”存储在二进制文件中(浪费空间)并检查每次迭代是否1 == 0(浪费时间和更多空间)。
但实际上,即使采用“不”优化,编译器仍然会将两者都降低到相同的水平。 它们也可能会发出警告,因为它可能表示存在逻辑错误。 例如,while的参数可以在其他地方定义,你没有意识到它是恒定的。
Nick T answered 2019-05-13T14:48:52Z
2 votes
我很惊讶没有人提供更直接的形式,对应于所需的组件:
forever:
do stuff;
goto forever;
Phil Miller answered 2019-05-13T14:49:31Z
2 votes
until(0)是for(;;)的成语,被大多数编译器认可。
我很高兴看到perl也认识到until(0)。
J M D answered 2019-05-13T14:50:13Z
2 votes
总结for (;;)与while (1)的争论很明显,前者在较旧的非优化编译器时代更快,这就是为什么你倾向于在较旧的代码库中看到它,如Lions Unix源代码评论,但是在这个时代 badass优化编译器这些增益被优化掉了,因为后者比前者更容易理解,我相信它会更优选。
redbandit answered 2019-05-13T14:50:48Z
1 votes
刚刚遇到这个帖子(虽然已经晚了很多年)。
我想我找到了“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’.
基本上,这不是速度问题,而是可读性问题。 根据代码的字体/打印,一段时间内的第一(1)可能看起来像小写字母l。
即1 vs l。 (在某些字体中看起来相同)。
因此,虽然(1)可能看起来像一些while循环依赖于变量字母L.
虽然(true)也可以工作但是在一些较旧的C和嵌入式C的情况下,除非包含stdbool.h,否则尚未定义true / false。
Nick Law answered 2019-05-13T14:52:10Z
-3 votes
我认为两者在性能方面都是一样的。 但我更喜欢(1)为了便于阅读,但我怀疑为什么你需要一个无限循环。
bichonfrise74 answered 2019-05-13T14:52:46Z
-12 votes
他们是一样的。 还有更重要的问题值得思考。
Mark Ransom answered 2019-05-13T14:53:23Z