Welcome to MSDN Blogs Sign in | Join | Help

ToddHa's WebLog

Musings, notions, thoughts about technology and software.

News

  • Disclaimer
    The information in this weblog is provided "AS IS" with no warranties, and confers no rights. This weblog does not represent the thoughts, intentions, plans or strategies of my employer. It is solely my opinion. Inappropriate comments will be deleted at the authors discretion. All code samples are provided "AS IS" without warranty of any kind, either express or implied, including but not limited to the implied warranties of merchantability and/or fitness for a particular purpose.
Performance Tip-O-The-Day #1 : for[each]

This starts a new series (I hope) of performance tips that you may/may not know about.

This post is brought to you by Microsoft Mobile Development Handbook, in which I found this gem. It's on page 217 at the bottom if you have the book.

The basic idea is that foreach is slower than a for loop. I've pretty much always known this, but never really known how much slower it is. There's some overhead, sure, but I've always assumed it was negligible.

For this demonstration, I'm going to be targeting the full .NET framework as opposed to the compact framework just so I can take advantage of a couple classes that aren't available in the compact framework, but the idea applies to both.

So here's the basic test code.

void Test(List<int> data)
{
    TimeSpan start, end;
   
int total, max, milliseconds;
    Process p = Process.GetCurrentProcess();

    total = 0;
    start = p.TotalProcessorTime;
    // start test
    foreach (int i in data)
    {
        total += i;
    }
    // end test
   
end = p.TotalProcessorTime;
    milliseconds = end.Subtract(start).TotalMilliseconds;
    Console.WriteLine("foreach : " + milliseconds + " ms");

    total = 0;
    max = data.Count;
    start = p.TotalProcessorTime;
    // start test
    for (int ctr = 0; ctr < max; ctr++)
    {
        total += data[ctr];
    }
    // end test
    end = p.TotalProcessorTime;
    milliseconds = end.Subtract(start).TotalMilliseconds;
    Console.WriteLine("foreach : " + milliseconds + " ms");
}

This code just takes a list of integers and then does a foreach loop and a for loop, calculating (roughly) the time for each.

Next, on to the driver code.

// 100,000,000
int DataSetSize = 100000000;
int Runs = 10;

try
{
    List<int> dataset = new List<int>(DataSetSize);
    for (int i = 0; i < DataSetSize; i++)
    {
        dataset.Add(i);
    }

    for (int i = 0; i < Runs; i++)
    {
        Test(dataset);
        Console.WriteLine();
    }
}
catch (OutOfMemoryException)
{
    Console.WriteLine("Please shrink DataSetSize.");
}

I arbitrarily decided to run this 5 times. The first time is always slower, and that's expected. The OutOfMemoryException is in there because I made the data set a little bit too big sometimes and kept on crashing the process harshly.

Anyways, the results (in Release mode) are :

foreach loop for loop
Test Run #1 1250 187.5
Test Run #2 984.375 187.5
Test Run #3 765.625 218.75
Test Run #4 828.125 203.125
Test Run #5 765.625 218.75

All times are in milliseconds and are on my arbitrary workstation (nothing special on it : Vista, Visual Studio 2005). Note that the for loop can be 3 times faster than the foreach. Obviously, it depends how many items you're iterating over, but unless you really have to (i.e. the class only provides an iterator), I'd suggest not using the foreach.

As always, thoughts, comments, and questions are always welcome.

Edit (2007-07-06 11:18am PST) : Minor code fix, it's always a good idea to pass an initial size to Lists, among others.

Posted: Thursday, July 05, 2007 7:29 PM by toddha
Filed under: , ,

Comments

Mamanze said:

Could you add a third test for .ForEach? Should be somewhere in the middle I'd imagine.

# July 5, 2007 3:21 PM

Mighell's blog said:

Lo avevo letto tempo fa (ma non ricordo dove) ma in questo post viene dimostrato: The basic idea is that

# July 5, 2007 5:09 PM

Mighell's Mobile Blog said:

Lo avevo letto tempo fa (ma non ricordo dove) ma in questo post viene dimostrato: The basic idea is that

# July 5, 2007 6:16 PM

alikl said:

Loved that!

simple and illustrative

BTW, check nice checklist from PAG at

http://msdn2.microsoft.com/en-us/library/ms979052.aspx

Which says "Use for instead of foreach in performance-critical code paths."

# July 6, 2007 5:59 AM

NielsUll said:

Micro-benchmarks are always dangerous - you get into issues with the optimizer kicking in, cache hits/misses etc etc. So all results should be taken with a grain of salt.

In addition, it is essential to print out the total - otherwise a good optimizer may just remove the whole loop entirely.

I tried doing a test of .ForEach using this code:

data.ForEach(delegate(int obj) { total += obj; });

and interestingly, I got this for Count=100,000,000 (after 10 iterations):

foreach : 953,125 ms, total=887459712

for : 687,5 ms, total=887459712

.ForEach : 531,25 ms, total=887459712

So .ForEach is actually the fastest in this case.

Out of interest, I also tried the same for an int[] - here the results were

foreach : 140,625 ms, total=887459712

for : 171,875 ms, total=887459712

So according to this test, foreach is faster than for when dealing with plain arrays.

Now, don't go change all your code based on this advice - ALWAYS measure before optimizing.

# July 9, 2007 4:45 AM
Anonymous comments are disabled
Page view tracker