views:

2911

answers:

17

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 compile code can be vs. a script 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.

+24  A: 

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.

Bill the Lizard
#define EVER ;;for(EVER) I've always find that kind of amusing.
Tom
How about #define ever (;;) forever;
Martin Cote
Both seem more readable on the surface, but I try not to define new keywords for my maintenance programmer (usually me) to scratch his head over.
Bill the Lizard
@Bill the Lizard You can still read it out aloud as for (ever) ;)
phihag
@Martin that will not work, because #define's do not replace within a token, and `forever` is it's own token.
Professor_Calculus
+1 `while(true)` is more readable and therefore, all other things equal, better code than `for(;;)`.
+1  A: 

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

Mark Ransom
No links or explanation. Unhelpful, subjective and a little condescending.
cdmckay
well no proof but he is right. They both call the Opcode for jumping when false. (which would make it the same as goto but no one likes gotos)
Matthew Whited
I was unaware that only important questions where to be asked, my mistake was my first question.
Copas
Nice change... I love it!!!
Matthew Whited
Yes, I admit it is condescending. But seriously, even without any proof it is obvious that they are going to be in the same ballpark speedwise; if the question was about style there would be something to argue about. I was trying to make the point that on the list of things to worry about, this should really be at the bottom of the list.
Mark Ransom
@cdmkcay - but also: he's right...
peterchen
@peterchan: Just because he happened to be right doesn't justify his poor answer.
cdmckay
+ It is a little condescending. Personally, I try not to be, but I don't always succeed, when faced with such a question, if it's supposed to be serious. The sheer number of questions like this is amazing and a little disheartening.
Mike Dunlavey
StackOverflow wasn't built to be the home of only the most important questions. Even things that should be "at the bottom of the list" are worth answering and often (as this did) yield good discussion/answers. Being a jerk to someone who is new to the site when you have the high ground of 10k reputation is counter productive in every way.
Copas
I wasn't trying to be a jerk. I was trying to make a point. When I posted it I was trying for a kind of dark humor, and it is obvious that I failed; for that I apologize.
Mark Ransom
Thanks for the clarification, vote to old to retract on answer so +1 on comment.
Copas
+3  A: 

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

Charlie
+1  A: 

In today's applications, is one extra conditional comparison really going to matter? I'm not trying to be a smarta$$, but to me what you gain in readability far out weighs the potential performance hit.

JP Alioto
Yes, if you do it a billion times in a row, or if it makes the inner loop esceed the code cache. There are probably more chances a single instruction can create havoc.
peterchen
If you do it enough it matters but in this particular situation this is an academic question between some friends at work.
Copas
+6  A: 

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

Chris Bartow
Good to know for golfing. Otherwise a poor reason to choose a syntax.
Adam Bellaire
+26  A: 

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

L2:
        jmp     L2
Martin Cote
How do you get this assembly code?
Using GCC with the -S option (assemble, do not link)
Martin Cote
+105  A: 

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?

bdonlan
Just a discussion among co-workers mostly, you have put it soundly to bed. Thanks
Copas
++ That's a fun perl switch.
guns
+1:excellent answer!
none
try with B::Deparse, deparsing an infinite for loop returns a while loop :P
Kent Fredric
This is an interesting perspective on programming. One I would really like to be more familiar with.
Akers
"In perl, they result in the same opcodes"... yes, but which is faster? :-)
Greg
+1 so you're closer to the Great Answer Badge. Because it is one :)
Marko
+14  A: 

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.

sean e
Thanks! That's the fist good reason for my friends missunderstanding I've heard.
Copas
the warning is probably becuase you used while(1) instead of while(true)
Here Be Wolves
true is a constant. while (true) is a constant expression. For anyone interested, warning C4127 is documented here: http://msdn.microsoft.com/en-us/library/6t66728h(VS.80).aspx
sean e
+7  A: 

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

Kent Fredric
+9  A: 

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) {
    ;
    ;
  }
}
Richard Corden
+1 Good answer.
GMan
That doesn't pertain to the generated code or performance at all. The standard only defines functionality.Of course, performance will be the same.
Potatoswatter
That's a good point, but I still think my answer covers the question. The standard has the 'as-if' rule: It doesn't matter how the compiler implements something as long as you cannot tell. For these loops, it doesn't matter what code is generated but the performance should not be different, because if it is then the compiler is violating the `as-if` rule in this case.
Richard Corden
I don't believe it's true that a difference in performance violates the as-if rule. If it were, then compilers would not be permitted to speed up your code under the as-if rule, for example by re-ordering independent statements. Compilers in fact do exactly that. But my copy of the standard is way upstairs.
Steve Jessop
Very good answer.
Copas
A: 

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.

bichonfrise74
+2  A: 

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

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

JayDee
In what situation would until(0) be helpful?
Copas
until() is the opposite of while() just as unless() is the opposite of if().As suggested eslewhere in this thread, one might write:do { something...} while (! condition)An alternative might beuntil (condition) { something}
JayDee
+3  A: 

I hope this question is only "academic".

In my experience, software of any reasonable size is, as first written, riddled with avoidable function calls, causing large factors of slowdown.

I still hear people in all seriousness asking questions like this, which is like a drunk looking for his keys under the light of a pencil-beam "because that's where the light is".

Mike Dunlavey
This question just came from a chat at work. That's all, a co-worker had an (apparently bad) professor give him the idea that somehow for (;;) was better then while (1). I didn't buy it but wanted to prove it to him buy asking some smart people (you guys) to demonstrate it better then I had with the test code I generated. So yes purely "academic".
Copas
@Copas: Please forgive my negativity. And as for professors, I was one, and I think there is a systemic problem in higher ed that there is precious little quality control on what professors tell students.
Mike Dunlavey
@Mike Agreed, thanks for your contribution. I don't take offence to it being considered a bad question. By all rights it very well may be a bad question. But it is a real question that came up in everyday work. That appears to have spurred on some real thought provoking discourse.
Copas
@Copas:Having been a prof, and being dumb enough to worry about cosmic issues, I have the impression that the whole "world-view" of software performance is too focussed on low-level issues, and considers too little the wide wonderful world of ways to make software slow, like too much abstraction, too much data structure, too much reliance on notification-style architecture, and ignorance of things like code generation. Sorry for the tirade.
Mike Dunlavey
... nobody in their right mind would casually write an extra level of nested loop if they didn't have to, but they think nothing of invoking another layer of "abstraction", the wonderfulness of which will surely guarantee getting the program counter back in the forseeable future.
Mike Dunlavey
+2  A: 

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

Hans W
+1  A: 

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.

Nick T
A: 

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

forever:
     do stuff;
     goto forever;
Novelocrat
Dose that not end up with the same machine code as while 1 or for (;;) in say c?
Copas
+5  A: 

I heard it about this once.

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.

Jimmie Clark