0

Which is better for performance? This may not be consistent with other programming languages, so if they are different, or if you can answer my question with your knowledge in a particular language, please explain.

I will be using c++ as an example, but I would like to know how it works in java, c, or any other mainstream languages.

int x = 0;
 while (x < 10) {
    cout << x << "\n ";
    x++;
  }

VS

for ( int x = 1; x < 10; x++)   
 cout << x << "\n ";

Which one performs better? If it is the for loop, then lets say that there was an integer already declared that we could use in the while loop increment, that we didn't need to create just for the while loop?

example:

int age = 17; //this was made for something else in the code, not a while loop. But fortunately for us, our while loop just so happened to need the number 17.

 while (age < 25) {
        cout << age << "\n ";
        age++;
      }

Would this instance make the while loop a better choice than creating a for loop? And I have seen questions somewhat similar to this, but I do not believe that this is a duplicate, or any of the answers on those other questions answered my question.

I want an explanation on this question, explaining if it is compiler specific, how it works, or whatever is a good answer to this question.

Gabriel
  • 3,039
  • 6
  • 34
  • 44
  • 12
    This is premature optimization at its worst. _It doesn't matter_. – SLaks Nov 24 '11 at 20:02
  • Please do not optimize without profiling. You will not see any difference between these loops in 99.99% of all code you write. And if you're going to optimize something like this, at least look at the assembly. It's going to be different for the different kinds of loops. – Nicol Bolas Nov 24 '11 at 20:03
  • 3
    From a performance perspective, I highly doubt there is **any** difference at all. I'd worry more about readability. – NullUserException Nov 24 '11 at 20:03
  • In which case you'll have to look at the assmebly/IL code that is generated. In most cases the compiler will generate the same code and in the few other cases it'll only be some reordering. All in all no noticeable difference here. – Voo Nov 24 '11 at 20:07
  • @Voo Unfortunately I don't know any assembly – Gabriel Nov 24 '11 at 20:07
  • @DavidHeffernan make that an answer – Gabriel Nov 24 '11 at 20:08
  • possible duplicate of [What loop is faster, while or for](http://stackoverflow.com/questions/3629174/what-loop-is-faster-while-or-for) – Pubby Nov 24 '11 at 21:03
  • @Gabe: it's not assembly language. Just look at the output -- its readable. – Hovercraft Full Of Eels Nov 25 '11 at 01:05

9 Answers9

4

All sensible compilers should compile equivalent loops to identical assembly / IL code involving branches and jumps. (at least with optimizations enabled)

SLaks
  • 868,454
  • 176
  • 1,908
  • 1,964
  • Not necessarily. Compilers have been known to optimize for loops stronger. – Pubby Nov 24 '11 at 21:07
  • It's a *slightly* higher level statement.Perhaps I'm wrong, but that means compilers can be more eager to optimize it. – Pubby Nov 24 '11 at 21:50
3

On certain CPU architectures, the characteristics of the loop may provide opportunities for more optimization than the choice of for versus while.

In particular, FORTRAN and Pascal recommended a constant (rather than a variable) for the number of loop iterations which some CPUs can optimize. For example, on a Cyber (a 1970s Iron Dinosaur mainframe), a 15-bit register holds the for loop index which can easily be compared. A while instead uses one or two of the harder-to-access 60-bit registers. Also, a Cyber branch instruction is considerably more expensive than the loop housekeeping, or possibly the loop content. In such cases, a high level of optimization might unroll the loop to avoid all the overhead.

However, modern code generators don't work like they used to: source code is turned into an intermediate parse structure which abstracts such choices away. In short, for versus while makes no difference.

wallyk
  • 56,922
  • 16
  • 83
  • 148
2

I find it hard to imagine situations where the code samples you give would have different performance characteristics.

I do have a mild curiosity for you though. In Pascal like languages (e.g. Delphi) the loop limits are evaluated only once. This differs from the C like languages where the loop limits are evaluated each iteration. This can have performance implications but of course its trivial to write performant code in C like languages by introducing a local outside the loop.

For example:

Delphi

for i := 0 to List.Count-1 do
  DoStuff(List[i]);

List.Count is only evaluated once.

C++

for (int i=0; i<List.getCount(); i++)
  DoStuff(List.getItem(i));

Here, List.getCount() is called every time around the loop.

If it transpires that evaluating the loop limits is expensive then this difference can be relevant. Naturally it is trivial to evaluate List.getCount() outside the loop and store the result in a local variable.

Having compared the for loops of Pascal and C/C++ I would say that the Pascal version is very simplistic in comparison. This is not necessarily a bad thing because for more complex there is always while available.

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
  • Read your first paragraph "I do have a mild curiosity for you though" did you mean something else? – Gabriel Nov 24 '11 at 20:18
  • Not sure what you mean by that comment. Sorry. – David Heffernan Nov 24 '11 at 20:19
  • Interesting delphi tidbit. Although you should probably mention CSE if you bring up that difference. Also you don't even have to introduce a variable outside the loop scope: `for (int i=0, max=List.GetCount(); i < max; i++)` - would be the usual idiom in C/C++ code in my experience. – Voo Nov 24 '11 at 20:20
  • @Voo What is CSE? I plead ignorance of that! – David Heffernan Nov 24 '11 at 20:21
  • @DavidHeffernan "I do have a mild curiosity for you though" means that you are curious about me, as a person, which is strange. I think what you meant to say is something like "I share the same curiosity as you" or something like that – Gabriel Nov 24 '11 at 20:21
  • No, what I mean is that the tidbit about Delphi is a curiosity. I'm afraid to say I have little curiosity for you as an individual!! – David Heffernan Nov 24 '11 at 20:22
  • Oh sorry, [Common Subexpression Elimination](http://en.wikipedia.org/wiki/Common_subexpression_elimination). Basically the compiler tries to guarantee that `List` isn't changed in the loop body and that `getCount()` doesn't have any side effects in which case it then does the same as the programmer would do. Mostly only works for simple stuff like e.g. `getCount()` though mostly in combination with inlining. So for really complex methods doing it manually is still the way to go. – Voo Nov 24 '11 at 20:23
  • @Voo Well, I would have mentioned that if I was familiar with it. But I'm not. My experience is mainly with Delphi and so I don't think I've ever come across it – not an optimisation that Delphi does I think. – David Heffernan Nov 24 '11 at 20:25
  • Well really my fault - write out acronyms on their first usage should be the standard, sorry about that. No idea about delphi - C++ compilers do it and especially the Java JITs are really fond of it. But in practice it's not a particularly reliable optimization as soon as you get complicated loop bodies or methods, so if the function is expensive (which means complicated method..) one should do it manually. But for stuff like `myArr.length()` it works usually fine. – Voo Nov 24 '11 at 20:35
  • @Gabe Curiosity: "A strange or unusual object or fact." (secondary definition) – Aaron Dufour Nov 24 '11 at 20:44
2

The example you posted actually has different behavior for while and for.

This:

for (int x = 0; x < 100; ++x) {
  foo y;
  y.bar(x);
}

Is equivalent to this:

{ // extra blocks needed
  int x = 0;
  while (x < 100) {
    {
      foo y;
      y.bar(x);
    }
    ++x;
  }
}

And you can expect the performance to be identical. Without the extra braces the meaning is different, and so the assembly generated may be different.

While the differences between the two is nonexistent on a modern compiler, for may optimize better as states its behavior more explicitly.

Pubby
  • 51,882
  • 13
  • 139
  • 180
1

It is compiler specific, but I would imagine that in nearly all cases the performance will be the same for both approaches.

To be sure for a specific situation you should measure the performance, although before you spend many hours micro-optimizing code like this you should first be sure that this is in fact the bottleneck in your application (probably it isn't).

Your second example could be writen as a for loop with no initialization expression.

int age = 17; // This was made for something else in the code

for (; age < 25; age++) {
    cout << age << endl;
}
Mark Byers
  • 811,555
  • 193
  • 1,581
  • 1,452
1

I think compilers are optimized enough these days so it really doesn't makes a difference if you use a for loop or a while loop.

xBlue
  • 523
  • 2
  • 7
  • 15
1

I would suggest that you write a small example and time the difference. Then select whatever solution you find to be the quickest.

mantler
  • 859
  • 1
  • 7
  • 22
1

It won't make any difference with compiler optimization!

My 2cts, when it deals about loop implementation choice, it's important to use the one which suits the algorithm logic. It's easier to read the code.

ablm
  • 1,265
  • 10
  • 19
1

Your question as you give it is ill posed. The IO that you have inside the loops dominates all that the loop handling statements ever could add by at least one order of magnitude. My bet would even be that you can't measure any significant difference since it would not be distinguishable from the noise (in terms of measurement) that the IO produces.

The thing would only matter, if the code inside the loop would be really fast. Here fast meaning not even to have a memory read instruction, since then already CPU memory bandwidth would dominate, again.

So if you are really curious, and you seem to be, read the assembler and look at the code. If you only have two small functions with just each type of loop, the output is not so difficult to read, really, don't be scared. I'd suggest to do it with C, though, this is probably closest to the assembly and easiest for you to identify the parts.

You didn't tell us on what system/compiler you are. With gcc the options would be -S -O3 -march=native to have it produce a .s file.

Jens Gustedt
  • 76,821
  • 6
  • 102
  • 177