Foreach or For – That is the Question

The discussion about the preference difference between FOREACH and FOR is not new. We all know that FOREACH is slower, but not all know why.

When I started learning .NET, one person told me that FOREACH  is two times slower than FOR. He said this without any grounds. I took it for granted.

Eventually, I decided to explore the performance of both loops, and write this article to discuss nuances.
Let’s have a look at the following code:

The FOREACH is a syntax sugar. In this particular case, the compiler transforms this it into the following code:

Knowing this we can assume the reason of why FOREACH is slower than FOR:

  • A new object is being created. It is called Creator.
  • The MoveNext method is called on each iteration.
  • Each iteration accesses the Current property.

That’s it! However, it is not all as easy as it sounds.

Fortunately (or unfortunately), C#/CLR may perform optimizations at run time. The pro is that the code works faster. The con – developers should be aware of these optimizations.

The array is a type deeply integrated into CLR, and CLR provides a number of optimizations for this type. The FOREACH loop is an iterable entity, which is a key aspect of the performance. Later in the article, we will discuss how to iterate through arrays and lists with help of the Array.ForEach static method and the List.ForEach method.

Test Methods

Test conditions:

  • The “Optimize code” option is turned on.
  • The number of elements is equal to 100 000 000 (both in the array and list).
  • PC specification: Intel Core i-5 and 8 GB of RAM.

Arrays

Iterating Arrays

The diagram shows that FOR and FOREACH spend the same amount of time while iterating through arrays. And it is because CLR optimization converts FOREACH into FOR, and uses the length of the array as the maximum iteration boundary. It does not matter whether the array length is cached or not (when using FOR), the result is almost the same.

It may sound strange, but caching the array length may affect performance. While using array.Length as the iteration boundary, JIT tests the index to hit into the right border beyond the cycle. This check is performed only once.
It is very easy to destroy this optimization. The case when the variable is cached is hardly optimized.

Array.foreach demonstrated the worst results. Its implementation is quite simple:

Then why is it running so slow? It uses FOR under the hood. Well, the reason is in calling the ACTION delegate. In fact, a method is called on each iteration, which decreases performance. Moreover, the delegates are invoked not as fast as we would like.

Lists

Iterating Lists

The result is completely different. When iterating lists, FOR and FOREACH show different results. There is no optimization. FOR (with caching the length of the list) shows the best result, whereas FOREACH is more than 2 times slower. It is because it deals with MoveNext and Current under the hood. List.ForEach as well as Array.ForEach show worst result. Delegates are always called virtually. The implementation of this method looks like this:

Each iteration calls the Action delegate. It also checks whether the list is changed and if so, an exception is thrown.

List<T> internally uses an array-based model and the ForEach method uses the array index to iterate through, which is significantly faster than using the indexer.

Arrays vs Lists

Specific numbers

  1. The FOR loop without length caching and FOREACH work slightly faster on arrays than FOR with length caching.
  2. Array.Foreach is approximately 6 times slower than FOR / FOREACH.
  3. The FOR loop without length caching works 3 times slower on lists, comparing to arrays.
  4. The FOR loop with length caching works 2 times slower on lists, comparing to arrays.
  5. The FOREACH loop works 6 times slower on lists, comparing to arrays.

Here is a leader board for lists:

Lists Range

And for arrays:

Arrays Range

Conclusion

I really enjoyed this investigation, especially writing process, and I hope you have enjoyed it as well. As it turned out, FOREACH is faster on arrays than FOR with length chasing. Thanks JIT for optimizations. On list structures, FOREACH is slower than FOR.

The code looks better when using FOREACH, and modern processors allow using it. However, if you need to highly optimize your codebase, it is better to use FOR.

Timur Guev

Timur Guev

Timur is an experienced C# developer. For last three years, Timur has been developing the KSS (Kaspesky Subscription Service) highload system with the C#, SQL Server,and Azure technologies. At loose hours, Timur is teaching mathematics.
Timur Guev

Latest posts by Timur Guev (see all)

Timur Guev

Timur is an experienced C# developer. For last three years, Timur has been developing the KSS (Kaspesky Subscription Service) highload system with the C#, SQL Server, and Azure technologies. At loose hours, Timur is teaching mathematics.

  • Nick Lucas

    While it certainly looks like there’s a significant performance difference, in practice is the difference really that significant?

    It’s maybe reasonable to say that your typical operation on each item is going to have a much larger operational time than the difference in iteration time. I’d love to see these results with a realistic Action operation one each iterator – more than just summing the values, for instance.

  • Phil Bolduc

    I recommend you take a look BenchmarkDotNet – https://github.com/dotnet/BenchmarkDotNet It takes into consideration warm ups and memory allocations/garbage collection.

    I ran a variation of your test with 1M elements. Here is the output.

    https://uploads.disquscdn.com/images/cf13ba2201a79e634ca9773eae0f17710e998cbfb77f133b9c3147288a58b08d.png

    Source:

    using System;
    using System.Linq;
    using BenchmarkDotNet.Attributes;
    using Ploeh.AutoFixture;

    public class ForVsForEach
    {
    private int[] array;

    [Params(1000000)]
    public int Count { get; set; }

    [Setup]
    public void Setup()
    {
    Fixture fixture = new Fixture();
    array = fixture.CreateMany(Count).ToArray();
    }

    [Benchmark]
    public long ArrayForWithoutOptimization()
    {
    long sum = 0;
    for (int i = 0; i < array.Length; i++)
    sum += array[i];
    return sum;
    }

    [Benchmark]
    public long ArrayForWithOptimization()
    {
    int length = array.Length;
    long sum = 0;
    for (int i = 0; i { sum += i; });
    return sum;
    }
    }