programing tip

(1) 대.

itbloger 2020. 6. 9. 08:22
반응형

(1) 대. for (;;) 속도 차이가 있습니까?


긴 버전 ...

동료는 오늘 while (1)Perl 스크립트에서 for (;;)더 빠른 사용을보고 나서 주장했다 . 나는 그들이 통역사가 어떤 차이를 최적화하길 바라고 같은 것이되어야한다고 주장했다. 루프 반복 및 동일한 수의 while 루프에 대해 1,000,000,000을 실행하고 그 사이의 시간을 기록하는 스크립트를 설정했습니다. 나는 눈에 띄는 차이를 찾을 수 없었다. 저의 동료는 교수가 그에게 while (1)비교를 1 == 1하고 있지만 for (;;)그렇지 않다고 그에게 말했습니다 . 우리는 C ++로 반복 횟수를 100 배로 동일한 테스트를 반복했으며 그 차이는 무시할 만했습니다. 그러나 컴파일 된 코드가 스크립팅 언어에 비해 얼마나 빠른지를 보여주는 그래픽 예입니다.

짧은 버전 ...

탈출하기 위해 무한 루프가 필요한 경우 while (1)오버 오버 를 선호하는 이유 for (;;)가 있습니까?

참고 : 질문에서 명확하지 않은 경우. 이것은 순전히 두 친구 간의 재미있는 학문적 토론이었습니다. 나는 이것이 모든 프로그래머가 고민해야 할 매우 중요한 개념이 아니라는 것을 알고 있습니다. 나는 (그리고 다른 사람들이)이 토론에서 몇 가지를 배운 모든 위대한 답변에 감사드립니다.

업데이트 : 위에서 언급 한 동료의 무게는 다음과 같습니다.

그것이 묻힐 경우를 대비하여 여기에 인용하십시오.

AMD 어셈블리 프로그래머가 제공 한 것입니다. 그는 C 프로그래머들 (사람들)은 그들의 코드가 비효율적이라는 것을 깨닫지 못한다고 말했다. 그는 오늘 gcc 컴파일러는 매우 훌륭하며 그와 같은 사람들을 사업에서 제외 시켰다고 말했다. 그는 예를 while 1들어서 vs 에 대해 이야기했습니다 for(;;). 지금은 습관이 없지만 gcc와 특히 통역사는 최적화되어 있기 때문에 요즘 두 가지 모두 동일한 작업 (프로세서 점프)을 수행합니다.


펄에서는 동일한 opcode가 발생합니다.

$ 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 <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

그래서 대답은 많은 컴파일러에서 동일하다는 것입니다. 물론 다른 컴파일러의 경우 반드시 그런 것은 아니지만 루프 내부의 코드가 루프 자체보다 수천 배 비싸 질 가능성이 있습니다.


GCC를 사용하면 둘 다 동일한 어셈블리 언어로 컴파일되는 것처럼 보입니다.

L2:
        jmp     L2

다른 것을 선호하는 이유는별로 없습니다. 나는 보다 읽기 쉽고 while(1)특히 while(true)더 읽기 쉽다고 생각 for(;;)하지만 그것은 단지 내 취향입니다.


표준에 따라 차이가 없습니다. 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) {
    ;
    ;
  }
}

에 대한 경고를 발생시키는 데 사용되는 Visual C ++ 컴파일러

while (1) 

(일정한 표현)

for (;;)

나는 for (;;)그 이유 를 선호하는 관행을 계속 했지만 컴파일러가 여전히 그렇게하는지 모르겠습니다.


for(;;) is one less character to type if you want to go in that direction to optimize things.


Turbo C with this old compilers for(;;) results in faster code then while(1).

Today gcc, Visual C (I think almost all) compilers optimize well, and CPUs with 4.7 MHz are rarely used.

In those days a for( i=10; i; i-- ) was faster than for( i=1; i <=10; i++ ), because compare i is 0, results in a CPU-Zero-Flag conditional Jump. And the Zero-Flag was modified with the last decrement operation ( i-- ), no extra cmp-operation is needed.

    call    __printf_chk
    decl    %ebx          %ebx=iterator i 
    jnz     .L2
    movl    -4(%ebp), %ebx
    leave

and here with for(i=1; i<=10; i++) with extra cmpl:

    call    __printf_chk
    incl    %ebx
    cmpl    $11, %ebx
    jne     .L2
    movl    -4(%ebp), %ebx
    leave

For all the people arguing you shouldn't use indefinte while loops, and suggesting daft stuff like using open goto's ( seriously, ouch )

while (1) {
     last if( condition1 );
     code();
     more_code(); 
     last if( condition2 ); 
     even_more_code(); 
}

Can't really be represented effectively any other way. Not without creating an exit variable and doing black magic to keep it synced.

If you have a penchant for the more goto-esque syntax, use something sane that limits scope.

flow: { 

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

Ultimately Speed is not that important

Worring about how effective speed wise different looping constructs are is a massive waste of time. Premature optimization through and through. I can't think of any situation I've ever seen where profiling code found bottlenecks in my choice of looping construct.

Generally its the how of the loop and the what of the loop.

You should "optimize" for readability and succinctness, and write whatever is best at explaining the problem to the next poor sucker who finds your code.

If you use the "goto LABEL" trick somebody mentioned, and I have to use your code, be prepared to sleep with one eye open, especially if you do it more than once, because that sort of stuff creates horrifically spaghetti code.

Just because you can create spaghetti code doesn't mean you should


From Stroustrup, TC++PL (3rd edition), §6.1.1:

The curious notation for (;;) is the standard way to specify an infinite loop; you could pronounce it "forever". [...] while (true) is an alternative.

I prefer for (;;).


If compiler doesn't do any optimization, for(;;) would always be faster than while(true). This is because while-statement evaluates the condition everytime, but for-statement is an unconditional jump. But if compiler optimizes the control flow, it may generate some opcodes. You can read disassembly code very easily.

P.S. you could write a infinite loop like this:

#define EVER ;;
  //...
  for (EVER) {
    //...
  }

I heard about this once.

It came from an AMD assembly programmer. He stated that C programmers (the people) 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.


In an optimized build of a compiled language, there should be no appreciable difference between the two. Neither should end up performing any comparisons at runtime, they will just execute the loop code until you manually exit the loop (e.g. with a break).


I am surprised that nobody properly tested for (;;) versus while (1) in perl!

Because perl is interpreted language, the time to run a perl script does not only consist of the execution phase (which in this case is the same) but also of the interpretation phase before execution. Both of these phases have to be taken in account when making a speed comparison.

Luckily perl has a convenient Benchmark module which we can use to implement a benchmark such as follows:

#!/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 });

Note that I am testing two different versions of the infinite for loop: one which is shorter than the while loop and another one which has an extra space to make it the same length as the while loop.

On Ubuntu 11.04 x86_64 with perl 5.10.1 I get the following results:

          Rate   for  for2 while
for   100588/s    --   -0%   -2%
for2  100937/s    0%    --   -1%
while 102147/s    2%    1%    --

The while loop is clearly the winner on this platform.

On FreeBSD 8.2 x86_64 with perl 5.14.1:

         Rate   for  for2 while
for   53453/s    --   -0%   -2%
for2  53552/s    0%    --   -2%
while 54564/s    2%    2%    --

While loop is the winner here too.

On FreeBSD 8.2 i386 with perl 5.14.1:

         Rate while   for  for2
while 24311/s    --   -1%   -1%
for   24481/s    1%    --   -1%
for2  24637/s    1%    1%    --

Surprisingly the for loop with an extra space is the fastest choice here!

My conclusion is that the while loop should be used on x86_64 platform if the programmer is optimizing for speed. Obviously a for loop should be used when optimizing for space. My results are unfortunately inconclusive regarding other platforms.


In theory, a completely naive compiler could store the literal '1' in the binary (wasting space) and check to see if 1 == 0 every iteration (wasting time and more space).

In reality, however, even with "no" optimizations, compilers will still reduce both to the same. They may also emit warnings because it could indicate a logical error. For instance, the argument of while could be defined somewhere else and you not realize it's constant.


I'm surprised no one has offered the more direct form, corresponding to the desired assembly:

forever:
     do stuff;
     goto forever;

while(1) is an idiom for for(;;) which is recognized by most compilers.

I was glad to see that perl recognizes until(0), too.


To summarize the for (;;) vs while (1) debate it is obvious that the former was faster in the days of older non-optimizing compilers, that is why you tend to see it in older code bases such as Lions Unix Source code commentary, however in the age of badass optimizing compilers those gains are optimized away coupling that with the fact that the latter is easier to understand than the former I believe that it would be more preferable.


Just came across this thread (although quite a few years late).

I think I found the actual reason why "for(;;)" is better than "while(1)".

according to the "barr coding standard 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’.

basically, this is not a speed issue but a readability issue. Depending on the font/print of code the number one(1) in a while may look like a lower case letter l.

i.e 1 vs l. (in some fonts these look identical).

So while(1) may look like some while loop dependent on the variable letter L.

while(true) may also work but in some older C and embedded C cases true/false are not yet defined unless stdbool.h is included.


I would think that both are the same in terms of performance. But I would prefer while(1) for readability but I question why you need an infinite loop.


They are the same. There are much more important questions to ponder.


암시되었지만 명시 적으로 위에서 언급하지 않은 요점은 괜찮은 컴파일러가 두 루프 형식 모두에 대해 동일한 코드를 생성한다는 것입니다. 더 큰 요점은 루핑 구조가 알고리즘 실행 시간의 작은 부분이므로 먼저 알고리즘과 관련 알고리즘을 모두 최적화했는지 확인해야합니다. 루프 구성을 최적화하는 것은 우선 순위 목록의 맨 아래에 있어야합니다.

참고 URL : https://stackoverflow.com/questions/885908/while-1-vs-for-is-there-a-speed-difference

반응형