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 and could increase performance if there is a sugnificant load with processing the data.
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. 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 there is load during the processing of the data. In all my open-source projects and any code that I work on in my contracts, I am always using for from now on.
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.
Hi,
Can you provide test source code ?
can you try add using the new ForEach method extention from linq? is the same as foreach? https://csharp-extension.com/en/method/1002639/array-foreach
Hi Dave, using foreach with AsParallel() is not how it is supposed to be used. Instead you should use AsParallel().ForAll.
I got the following results on 1k items with 100ms delay on each iteration:
Execution with [for] took 109500ms
Execution with [foreach AsParallel] took 109378ms
Execution with [AsParallel().ForAll] took 13734ms
Here is my source for this test:
Console.WriteLine(“AsParallel Test”);
var arr = new int[1000];
Stopwatch stp = new Stopwatch();
stp.Start();
for (int i = 0; i {
Task.Delay(100).Wait();
});
stp.Stop();
Console.WriteLine($”Execution with [AsParallel().ForAll] took {stp.ElapsedMilliseconds}ms”);
Console.WriteLine(“Press any key to exit”);
Console.ReadKey();
“I would also avoid using foreach with AsParallel(),”
The code sample that is being run here is very basic and I suspect the overhead of creating new threads isn’t worth the cost for this. I expect it would be better if the internal workload inside each loop iteration was heavier. You would then get much more benefit from multi threading as each process would be spending more time doing the work rather than starting and stopping threads.