For vs Foreach part 2

After receiving some questions and talking it through with my coworkers at Caped Koala Studios (the edutainment company behind www.poraora.com) I’ve decided to expand on the For vs Foreach question. This is part two of the article and you can read the previous one here.

Today I’ve asked a friend to compile and run the code on his Macbook Pro and on a iPad 2. First I’ll present the results and then delve deeper with ILSpy trying to explain what we see. You can skip to the interesting part right away.

Statistics

Test machine:

  • 32 GHz Intel Core i7
  • OS X Yosemite 10.10.4
  • Unity 5.1.2f1, Mac OS X Standalone, x86_64, non-development,
  • 640×480, Fastest, Windowed
Size Array For Array Foreach List For List Foreach
100 12 26 39 98
1000 121 270 354 901

 

Similarly like with windows build in the x86 version the for and foreach perform very similarly:

Size Array For Array Foreach List For List Foreach
100 13 13 52 121
1000 131 142 484 1067

 

And the results for iPad 2:

Size Array For Array Foreach List For List Foreach
100 126 197 533 1097
1000 1240 1964 5284 10135


 
 

The interesting part

So what’s really happening here?

I’ve went on and used the ILSpy to look under the hood. Looking at the C# generated from the decompiled IL (just for the sake of readability) we can see something interesting here.

Array For loop Source:

Array For loop ILSpy decompiled:

Nothing too shocking happening here. It’s worth to note that we have nicely cached the array.Length here so that the property accessor is called only once. Exchanging for with while is just notation difference really. So let’s look at the array foreach now.

Array Foreach loop Source:

Array Foreach loop ILSpy decompiled:

Not too suprisingly the array foreach is just a language feature. Under the hood it’s translated into a simple for but with two differences to actually using for:

  1. We get a copy of the array pointer
  2. We don’t cache the array.Length so we call the property accessor every iteration

This is the possible explanation of why the array foreach is slower than for – but why do we see the difference only with the 64b version?

Conclusions

  • The array foreach is slower than using for but the difference is not as big as with lists and it seems not to affect the 32b builds as much as the 64b ones.
  • The list for is slower than the array’s because of the item accessor performing additional checks.
  • The List foreach is way slower than the for because it calls lots of virtual methods during the iteration. Like the List’s GetEnumerator() (which actually allocates the enumerator) and then the GetCurrent() and MoveNext() methods of the enumerator. You can read more about the Array vs List performance on Jackson Dunstan’s blog which is well worth following.

The main take away from this article is: For the time being, it’s safe to just keep using for as the loop of choice in performance critical areas.

Thanks for reading :)

2 Comments

  1. Jackson Dunstan

    Very interesting results! I too was curious why there is a difference between your results and mine, so I ran my original program through ILSpy to see. Here’s the array for loop:

    int j = 0;
    int num = array.Length;
    while (j < num)
    {
    object obj = array[j];
    j++;
    }

    That looks pretty much like yours, except that the counter increment isn't there. Still, the compiler didn't recognize that the loop does nothing and remove it entirely, though that could still be done by the VM.

    Now here's the foreach loop over the array:

    object[] array2 = array;
    for (int l = 0; l < array2.Length; l++)
    {
    object obj2 = array2[l];
    }

    This version also ended up pretty much like yours, again minus the counter increment. Again, the whole loop wasn't stripped out by the compiler.

    So I wonder- to what do you attribute the performance differences between our two versions?

    Reply
    1. Krzysiek (Post author)

      Actually, I’ve just tried again and have run the code exactly as is from your blog post.

      The result:

      Size,Array For Time,Array Foreach Time,List For Time,List Foreach Time
      10,0,1,3,18
      100,7,11,32,90
      1000,68,93,326,831

      The performance is actually similar to what I get in the slightly changed code tested above.

      I don’t understand how did you get array foreach to be faster than for in your tests. Any ideas?

      Reply

Leave a Comment

Your email address will not be published. Required fields are marked *