while (1) Vs. for (;;) Is there a speed difference?

C++PerlOptimizationPerformance

C++ Problem Overview


Long version...

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.

Short version...

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.

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

C++ Solutions


Solution 1 - C++

In perl, they result in the same opcodes:

$ 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

Likewise in 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

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?

Solution 2 - C++

There's not much reason to prefer one over the other. I do think that while(1) and particularly while(true) are more readable than for(;;), but that's just my preference.

Solution 3 - C++

Using GCC, they both seem to compile to the same assembly language:

L2:
        jmp     L2

Solution 4 - C++

There is no difference according to the standard. 6.5.3/1 has:

>The for statement

for ( for-init-statement ; conditionopt ; expressionopt ) statement

>is equivalent to

{
  for-init-statement
  while ( condition ) {
    statement
    expression ;
  }
}

And 6.5.3/2 has:

>Either or both of the condition and the expression can be omitted. A missing condition makes the implied while clause equivalent to while(true).

So according to the C++ standard the code:

for (;;);

is exactly the same as:

{
  while (true) {
    ;
    ;
  }
}

Solution 5 - C++

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

Solution 6 - C++

The Visual C++ compiler used to emit a warning for

while (1) 

(constant expression) but not for

for (;;)

I've continued the practice of preferring for (;;) for that reason, but I don't know if the compiler still does that these days.

Solution 7 - C++

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

Solution 8 - C++

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

Solution 9 - C++

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 (;;).

Solution 10 - C++

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) {
    //...
  }

Solution 11 - C++

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.

Solution 12 - C++

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).

Solution 13 - C++

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.

Solution 14 - C++

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.

Solution 15 - C++

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

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

Solution 16 - C++

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.

Solution 17 - C++

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

forever:
     do stuff;
     goto forever;

Solution 18 - C++

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.

Solution 19 - C++

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.

Solution 20 - C++

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


My point which was implied but not explicitly made above, is that a decent compiler would generate the exact same code for both loop forms. The bigger point is that the looping construct is a minor part of the run time of any algorithm, and you must first ensure that you have optimized the algorithm and everything else related to it. Optimizing your loop construct should absolutely be at the bottom of your priority list.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionCopasView Question on Stackoverflow
Solution 1 - C++bdonlanView Answer on Stackoverflow
Solution 2 - C++Bill the LizardView Answer on Stackoverflow
Solution 3 - C++Martin CoteView Answer on Stackoverflow
Solution 4 - C++Richard CordenView Answer on Stackoverflow
Solution 5 - C++Chris BartowView Answer on Stackoverflow
Solution 6 - C++sean eView Answer on Stackoverflow
Solution 7 - C++Lutz L.View Answer on Stackoverflow
Solution 8 - C++Kent FredricView Answer on Stackoverflow
Solution 9 - C++Hans WView Answer on Stackoverflow
Solution 10 - C++silverbulletttView Answer on Stackoverflow
Solution 11 - C++Jimmie ClarkView Answer on Stackoverflow
Solution 12 - C++CharlieView Answer on Stackoverflow
Solution 13 - C++snapView Answer on Stackoverflow
Solution 14 - C++Nick LawView Answer on Stackoverflow
Solution 15 - C++J M DView Answer on Stackoverflow
Solution 16 - C++Nick TView Answer on Stackoverflow
Solution 17 - C++Phil MillerView Answer on Stackoverflow
Solution 18 - C++redbanditView Answer on Stackoverflow
Solution 19 - C++bichonfrise74View Answer on Stackoverflow
Solution 20 - C++Mark RansomView Answer on Stackoverflow