Friday, March 28, 2008

Are ForEach Loops Slower Then For Loops?

While reading some of Microsoft's patterns and practices material on optimizing .NET code I ran across an interesting comment. Microsoft recommends using for instead of foreach in performance sensitive loops.

To test I started out using the ArrayList on a bunch of strings. To get the best accuracy I P/Invoked GetTickCount . Here is what I found:

Collection SizeForeach Loop TicksFor Loop TicksPerformance Gain
5,000,0001099317.20%
10,000,00023417236.05%
20,000,00046835830.73%
50,000,000120292030.65%


Although the lower numbers were a bit sporadic the for loop did seem to function roughly 30% faster then the foreach. Lets look at what the foreach is doing. First lets look at a simple foreach loop:


foreach (string str in arr)
{
// Do Something
}


What is the foreach operator doing? It's calling GetEnumerator from arr and then calling MoveNext on each iteration.


IEnumerator enumerator = arr.GetEnumerator();
while (enumerator.MoveNext())
{
string str = (string)enumerator.Current;
// Do Something
}


MoveNext is called on each iteration of the loop. Using Lutz Roeder's .NET Reflector we can see what MoveNext is doing.


public bool MoveNext()
{
if (this.version != this.list._version)
{
throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_EnumFailedVersion"));
}
if (this.isArrayList)
{
if (this.index < (this.list._size - 1))
{
this.currentElement = this.list._items[++this.index];
return true;
}
this.currentElement = dummyObject;
this.index = this.list._size;
return false;
}
if (this.index < (this.list.Count - 1))
{
this.currentElement = this.list[++this.index];
return true;
}
this.index = this.list.Count;
this.currentElement = dummyObject;
return false;
}


We could step through this but lets look at only the lines that are run:



public bool MoveNext()
{
if (this.version != this.list._version) { }
if (this.isArrayList)
{
if (this.index < (this.list._size - 1))
{
this.currentElement = this.list._items[++this.index];
return true;
}
}
}


So it looks like our loop execution is being slowed down by some properties and conditional statements. This is insightful, but do we really care? Well if you are still reading this you might. If you are in the rare case that you need a insanely fast loop and you need precious milliseconds when looping millions of records you might want to replace your foreach loops with for loops. As for the sane rest of us I'm sure we can get a better return if we optimize the code within the loop.

Supplemental Links:
Loop Test Code

Save to del.icio.us Add to Technorati Add to dzone

No comments: