c语言leb128编码,c ++ - while(1)Vs. for(;;)有速度差吗?

博客探讨了C++中无限循环`while(1)`和`for(;;)`的速度差异。作者进行了一系列测试,发现两者在现代编译器下几乎没有速度差异,因为编译器优化使得它们在执行时表现相同。讨论还涉及了可读性和编程习惯,以及在Perl中的等价实现。结论是,在性能上没有显著区别,但`for(;;)`可能因简洁性而更受欢迎。
摘要由CSDN通过智能技术生成

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值