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

2021-12-08 00:00:00 performance optimization perl c++

长版...

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

A co-worker asserted today after seeing my use of while (1) in a Perl script that for (;;) is faster. I argued that they should be the same hoping that the interpreter would optimize out any differences. I set up a script that would run 1,000,000,000 for loop iterations and the same number of while loops and record the time between. I could find no appreciable difference. My co-worker said that a professor had told him that the while (1) was doing a comparison 1 == 1 and the for (;;) was not. We repeated the same test with the 100x the number of iterations with C++ and the difference was negligible. It was however a graphic example of how much faster compiled code can be vs. a scripting language.

简短版本...

如果您需要摆脱无限循环,是否有任何理由更喜欢 while (1) 而不是 for (;;)?

Is there any reason to prefer a while (1) over a for (;;) if you need an infinite loop to break out of?

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

Note: If it's not clear from the question. This was purely a fun academic discussion between a couple of friends. I am aware this is not a super important concept that all programmers should agonize over. Thanks for all the great answers I (and I'm sure others) have learned a few things from this discussion.

更新:上述同事权衡了以下回复.

Update: The aforementioned co-worker weighed in with a response below.

在这里引用以防它被掩埋.

Quoted here in case it gets buried.

它来自一个 AMD 汇编程序员.他说 C 程序员(人们)没有意识到他们的代码效率低下.他说不过今天,gcc 编译器非常好,把像他这样的人赶出去的业务.例如,他说,并告诉我 while 1for(;;).我现在出于习惯使用它,但是 gcc 尤其是解释器这两天将执行相同的操作(处理器跳转),因为它们已经过优化.

It came from an AMD assembly programmer. He stated that C programmers (the poeple) don't realize that their code has inefficiencies. He said today though, gcc compilers are very good, and put people like him out of business. He said for example, and told me about the while 1 vs for(;;). I use it now out of habit but gcc and especially interpreters will do the same operation (a processor jump) for both these days, since they are optimized.

推荐答案

在 perl 中,它们产生相同的操作码:

In perl, they result in the same opcodes:

$ perl -MO=Concise -e 'for(;;) { print "foo
" }'
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
"] s ->7
8           <0> unstack v ->4
-e syntax OK

$ perl -MO=Concise -e 'while(1) { print "foo
" }'
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
"] s ->7
8           <0> unstack v ->4
-e syntax OK

同样在 GCC 中:

#include <stdio.h>

void t_while() {
    while(1)
        printf("foo
");
}

void t_for() {
    for(;;)
        printf("foo
");
}

    .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

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

So I guess the answer is, they're the same in many compilers. Of course, for some other compilers this may not necessarily be the case, but chances are the code inside of the loop is going to be a few thousand times more expensive than the loop itself anyway, so who cares?

相关文章