而(1)Vs. for(;;)有速度差吗?

Cop*_*pas 152 c++ optimization perl performance

长版......

一位同事在看到我while (1)在Perl脚本中使用for (;;)更快后断言了.我认为他们应该是一样的,希望翻译能够优化任何差异.我设置了一个脚本,它将运行1,000,000,000次循环迭代和相同数量的while循环并记录它们之间的时间.我找不到明显的区别.我的同事说,一位教授告诉他,这while (1)是在进行比较1 == 1而事实for (;;)并非如此.我们用100倍的C++迭代次数重复相同的测试,差异可以忽略不计.然而,它是一个图形示例,说明编译代码与脚本语言相比可以更快.

精简版...

没有任何理由,更喜欢一个while (1)比一个for (;;),如果你需要一个无限循环打出来的?

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

更新:前面提到的同事在下面做出回应.

这里引用以防它被埋没.

它来自AMD汇编程序员.他说C程序员(人们)没有意识到他们的代码效率低下.他今天说,gcc编译器非常好,让像他这样的人破产.他举例称,并告诉我关于while 1VS for(;;).我现在出于习惯而使用它,但是gcc和特别是解释器在这些日子里都会做同样的操作(处理器跳转),因为它们已经过优化.

bdo*_*lan 216

在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
Run Code Online (Sandbox Code Playgroud)

同样在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
Run Code Online (Sandbox Code Playgroud)

所以我猜答案是,它们在许多编译器中都是一样的.当然,对于其他一些编译器,情况可能不一定如此,但是循环内部的代码可能比循环本身要贵几千倍,所以谁在乎呢?

  • "在perl中,它们导致相同的操作码"......是的,但哪个更快?:-) (24认同)
  • 尝试使用B :: Deparse,去除无限for循环返回while循环:P (14认同)
  • 我喜欢gcc用printf()代替puts(),因为只有一个参数,因此无需格式化 - 更快更安全!(gcc还会根据变量参数列表检查格式化标记.) (5认同)

Mar*_*ote 55

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

L2:
        jmp     L2
Run Code Online (Sandbox Code Playgroud)

  • 使用带-S选项的GCC(汇编,不链接) (19认同)

Bil*_*ard 53

没有太多理由偏爱一个而不是另一个.我确实认为,while(1)尤其while(true)是更具可读性for(;;),但这只是我的偏好.

  • #define EVER ;; 因为(EVER)我总能找到那种有趣的东西. (92认同)
  • #define永远如此(;;); (19认同)
  • 从表面上看,两者看起来都更具可读性,但我尽量不为我的维护程序员(通常是我)定义新的关键词. (16认同)
  • @Martin不起作用,因为#define不会在令牌中替换,而`forever`是它自己的令牌. (13认同)
  • "我尽量不为我的维护定义新的关键词" - 如果只有更多的人采取这种态度,我每次转身时都不会抓住所有这些愚蠢的魔法手法诡计! (2认同)

Ric*_*den 31

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

for语句

for ( for-init-statement ; conditionopt ; expressionopt ) statement
Run Code Online (Sandbox Code Playgroud)

相当于

{
  for-init-statement
  while ( condition ) {
    statement
    expression ;
  }
}
Run Code Online (Sandbox Code Playgroud)

6.5.3/2有:

可以省略条件和表达式中的任何一个或两者.缺失条件使隐含的while子句等同于while(true).

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

for (;;);
Run Code Online (Sandbox Code Playgroud)

与以下内容完全相同:

{
  while (true) {
    ;
    ;
  }
}
Run Code Online (Sandbox Code Playgroud)

  • 这与生成的代码或性能完全无关.该标准仅定义功能.当然,表现会是一样的. (4认同)
  • 我不认为性能差异确实违反了假设规则。如果是,那么编译器将不允许在 as-if 规则下加速代码,例如通过重新排序独立语句。事实上,编译器正是这样做的。但我的标准副本在楼上。 (2认同)

sea*_*n e 28

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

while (1) 
Run Code Online (Sandbox Code Playgroud)

(常数表达式)但不是

for (;;)
Run Code Online (Sandbox Code Playgroud)

for (;;)由于这个原因,我继续选择优先考虑的做法,但我不知道这些天编译器是否仍然这样做.

  • true是常数.while(true)是一个常量表达式.对于任何感兴趣的人,请在此处记录警告C4127:http://msdn.microsoft.com/en-us/library/6t66728h(VS.80).aspx (16认同)
  • @ElvissStrazdins Visual Studio 的文档现在表示 C4127 不会发出“while(1)”或“while(true)”的警告。 (2认同)

Chr*_*tow 26

for(;;) 如果你想沿着那个方向去优化事物,那么输入的字符就少了一个.

  • 很高兴知道打高尔夫球.否则选择语法的原因很差. (21认同)

Lut*_* L. 20

使用这些旧编译器的Turbo C会for(;;)产生更快的代码while(1).

今天gcc,Visual C(我认为几乎所有)编译器都很好地优化了,很少使用4.7 MHz的CPU.

在那些日子里,a 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
Run Code Online (Sandbox Code Playgroud)

这里for(i=1; i<=10; i++)有额外的cmpl:

    call    __printf_chk
    incl    %ebx
    cmpl    $11, %ebx
    jne     .L2
    movl    -4(%ebp), %ebx
    leave
Run Code Online (Sandbox Code Playgroud)


Ken*_*ric 13

对于所有人争论你不应该使用indefinte while循环,并建议像使用开放转到的愚蠢的东西(严重的,哎哟)

while (1) {
     last if( condition1 );
     code();
     more_code(); 
     last if( condition2 ); 
     even_more_code(); 
}
Run Code Online (Sandbox Code Playgroud)

无法以任何其他方式真正有效地表达.不是没有创建退出变量并且做黑魔法以保持同步.

如果您喜欢更多goto-esque语法,请使用限制范围的合理内容.

flow: { 

   if ( condition ){ 
      redo flow;
   }
   if ( othercondition ){ 
       redo flow;
   }
   if ( earlyexit ){ 
       last flow;
   }
   something(); # doesn't execute when earlyexit is true 
}
Run Code Online (Sandbox Code Playgroud)

最终速度并不重要

考虑速度明智的不同循环结构是多么有效浪费时间.过早优化通过.我想不出任何我见过的情况,在我选择的循环结构中,分析代码找到了瓶颈.

一般来说其如何循环和什么样的循环.

您应该"优化"可读性和简洁性,并将最好的解释问题的内容写入找到代码的下一个可怜的傻瓜.

如果你使用有人提到的"goto LABEL"技巧,我必须使用你的代码,准备好一只眼睛打开睡觉,特别是如果你不止一次这样做,因为那种东西会产生可怕的意大利面条代码.

仅仅因为你可以创建意大利面条代码并不意味着你应该


Han*_*s W 9

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

好奇的符号for (;;)是指定无限循环的标准方法; 你可以发音"永远".[...] while (true)是另一种选择.

我更喜欢for (;;).


sil*_*ttt 9

如果编译器没有进行任何优化,for(;;)那么总是会比while(true).这是因为while语句每次都会计算条件,但for-statement是无条件跳转.但是如果编译器优化控制流,它可能会生成一些操作码.您可以非常轻松地阅读反汇编代码.

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

#define EVER ;;
  //...
  for (EVER) {
    //...
  }
Run Code Online (Sandbox Code Playgroud)


Jim*_*ark 8

我曾经听过这个.

它来自AMD汇编程序员.他表示,C程序员(人员)没有意识到他们的代码效率低下.他今天说,gcc编译器非常好,让像他这样的人破产.他举例称,并告诉我关于while 1VS for(;;).我现在出于习惯而使用它,但是gcc和特别是解释器在这些日子里都会做同样的操作(处理器跳转),因为它们已经过优化.


Cha*_*lie 5

在编译语言的优化版本中,两者之间应该没有明显的差异.最后两者都不应该在运行时执行任何比较,它们只会执行循环代码,直到您手动退出循环(例如,使用a break).


Nic*_*Law 5

刚刚遇到这个线程(虽然晚了好几年)。

\n\n

我想我找到了“for(;;)”比“while(1)”更好的真正原因。

\n\n

根据“2018年巴尔编码标准”

\n\n
Kernighan & Ritchie long ago recommended for (;;) , which has the additional benefit\nof insuring against the visually-confusing defect of a while (l); referencing a variable \xe2\x80\x98l\xe2\x80\x99.\n
Run Code Online (Sandbox Code Playgroud)\n\n

基本上,这不是速度问题,而是可读性问题。根据代码的字体/打印,数字一(1)有时可能看起来像小写字母 l。

\n\n

即1 vs l。(在某些字体中,它们看起来相同)。

\n\n

所以 while(1) 可能看起来像一些依赖于变量字母 L 的 while 循环。

\n\n

while(true) 也可能有效,但在某些较旧的 C 和嵌入式 C 情况下,除非包含 stdbool.h,否则尚未定义 true/false。

\n

  • 我想说你的代码中的问题是你有一个名为“l”的变量,而不是“1”和“l”看起来相似。 (2认同)