Performance Tip: for vs. foreach in Microsoft .NET

Collections are one of the most commonly used types in programming. For any program that uses data, you will be dealing with collections. One of the most common things we do is to iterate over the collection to process the data. There are three main ways to iterate over a collection in .NET…

for

For is a common statement in many programming languages by using a counter variable to iterate over a collection.

for (int personCount = 0; personCount < _personCollection.Count - 1; 
         personCount++)
{
    var name = _personCollection[personCount].FirstName;
}

foreach

Foreach was the statement I usually used because it’s cleaner and easier to read.

foreach (var person in _personCollection)
{    
    var name = person.FirstName;
}

foreach with AsParallel()

AsParallel() is part of LINQ. It automagically moves the iteration over the collection to multiple cores in the CPU.

foreach (var person in _personCollection.AsParallel())
{    
    var name = person.FirstName;
}

Performance

I’ve done a lot of benchmarking using for, foreach and foreach AsParallel() for my book on code performance. I ran the benchmark four times using a collection count of 100, 500, 2000 and 5000. Included in this test is the comparison between .NET Clr 4.7.2 and .NET Core 2.2. Below are the results.

Test Runtime Collection Count Mean (ns) CLR vs CORE Difference
for Clr 4.7.2 100 108.8216
foreach Clr 4.7.2 100 308.1016
foreach AsParallel() Clr 4.7.2 100 853.7294
for Core 2.2 100 108.3985 -0.4231
foreach Core 2.2 100 334.339 26.2374
foreach AsParallel() Core 2.2 100 821.127 -32.60
for Clr 4.7.2 500 672.2033
foreach Clr 4.7.2 500 1,619.09
foreach AsParallel() Clr 4.7.2 500 3,881.51
for Core 2.2 500 671.033 -1.17
foreach Core 2.2 500 1,662.23 43.14
foreach AsParallel() Core 2.2 500 3,626.62 -254.89
for Clr 4.7.2 2000 3,400.98
foreach Clr 4.7.2 2000 7,615.78
foreach AsParallel() Clr 4.7.2 2000 18,726.19
for Core 2.2 2000 3,422.70 21.72
foreach Core 2.2 2000 7,444.41 -171.36
foreach AsParallel() Core 2.2 2000 13,814.03 -4,912.16
for Clr 4.7.2 5000 10,242.85
foreach Clr 4.7.2 5000 21,524.22
foreach AsParallel() Clr 4.7.2 5000 38,018.76
for Core 2.2 5000 10,330.39 87.54
foreach Core 2.2 5000 21,290.93 -233.29
foreach AsParallel() Core 2.2 5000 35,289.98 -2,728.78

The test was done using a business object called Person to mimic the a real world object. As you can see, using for is around 2-3 times faster than foreach! Wow, I was surprised when I first saw this. The benchmark comparing the .NET Clr 4.7.2 to .NET Core 3 produced similar results.

In most tests, .NET Core is faster than the Clr, especially for foreach AsParallel(). Moving to .NET Core should be on your team’s roadmap, if you aren’t already moving to it.

Summary

My recommendation is to always use for and avoid foreach. I would also avoid using foreach with AsParallel(), unless they are really needed, since it’s always slower than the other two in my testing. In all my open-source projects and any code that I work on in my contracts, I am always using for from now on, unless Microsoft comes up with a faster way.

Of course, you should always benchmark your code to see how for, foreach, do and while looping performs in your own projects and servers.

To learn more about this subject and a lot more, pick up a copy of my book Rock Your Code: Code & App Performance for Microsoft .NET. Do you have any performance tips when using collections? Please make a comment below.

Leave a Reply

Please log in using one of these methods to post your comment:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s