
The question of choosing a for / foreach loop is as old as the world. We all heard that foreach is slower for-a. But not everyone knows why ... But in general, is it?
When I started learning .NET, one person told me that foreach works 2 times slower for for, without any justification, and I took it for granted. Now that I have few words, I decided to write this article.
In this article, I explore the performance of cycles, as well as clarify some of the nuances.
')
So let's go!
Consider the following code:
foreach (var item in Enumerable.Range(0, 128)) { Console.WriteLine(item); }
The foreach loop is syntactic sugar, and in this case, the compiler expands it approximately to the following code:
IEnumerator<int> enumerator = Enumerable.Range(0, 128).GetEnumerator(); try { while (enumerator.MoveNext()) { int item = enumerator.Current; Console.WriteLine(item); } } finally { if (enumerator != null) { enumerator.Dispose(); } }
Knowing this, it is easy to guess why foreach should be slower than for-a. When using foreach:
- creates a new object - an iterator;
- at each iteration, the MoveNext method is called;
- at each iteration, the Current property is accessed, which is equivalent to calling the method;
That's all! But no, it's not that simple ...
There is one more nuance! Fortunately or, unfortunately, C # / CLR have a lot of optimizations. Fortunately, because the code is faster, unfortunately, because developers have to know about it (yet I think fortunately, and much more).
For example, since arrays are a type that is highly integrated in the CLR, there are a large number of optimizations for them. One of them concerns the foreach loop.
Thus, an important aspect of foreach loop performance is an iterable entity, since depending on it, it can unfold in different ways.
In the article we will consider iteration of arrays and lists. In addition to for-a and foreach, we will also consider iteration using the static method Array.ForEach, as well as the instance List.ForEach.
Testing methods static double ArrayForWithoutOptimization(int[] array) { int sum = 0; var watch = Stopwatch.StartNew(); for (int i = 0; i < array.Length; i++) sum += array[i]; watch.Stop(); return watch.Elapsed.TotalMilliseconds; } static double ArrayForWithOptimization(int[] array) { int length = array.Length; int sum = 0; var watch = Stopwatch.StartNew(); for (int i = 0; i < length; i++) sum += array[i]; watch.Stop(); return watch.Elapsed.TotalMilliseconds; } static double ArrayForeach(int[] array) { int sum = 0; var watch = Stopwatch.StartNew(); foreach (var item in array) sum += item; watch.Stop(); return watch.Elapsed.TotalMilliseconds; } static double ArrayForEach(int[] array) { int sum = 0; var watch = Stopwatch.StartNew(); Array.ForEach(array, i => { sum += i; }); watch.Stop(); return watch.Elapsed.TotalMilliseconds; }
Tests were performed when the flag is enabled to optimize the code in Release. The number of elements in the array and the list is 100,000,000. The machine on which the tests were conducted has an Intel Core i-5 processor and 8 GB of RAM on its board.
Arrays

From the diagram you can see that for / foreach the same time works on arrays. This is the work of the very optimization that deploys the foreach loop in for, using the array length as the maximum iteration boundary. By the way, it doesn't matter if we cache the length or not when iterating with for, the result is almost the same.
However strange it may be, but when using arrays, length caching can have a negative effect. The fact is that when JIT sees an array.Length as an iteration boundary in a loop, it makes an index check to hit the desired bounds per cycle, thus checking is done only once. This optimization is very easy to destroy, and the case when we cache a variable is no longer optimized.
The Array.ForEach method showed the worst result. Its implementation looks quite simple:
public static void ForEach<T>(T[] array, Action<T> action) { for (int index = 0; index < array.Length; ++index) action(array[index]); }
Then why does it work so slowly? After all, behind the scenes, he simply uses the usual for. It's all about calling the delegate action. In fact, at each iteration, the method is called, and we know that this is an extra overhead. Moreover, as you know, delegates are not called as quickly as we would like, hence the result.
With arrays all explained. Go to the lists.
For lists we will use similar methods of comparison.
Lists

Here is a completely different result !!!
When iterating through lists, for / foreach loops give different results. The fact is that there is no optimization here, and foreach takes place in the creation of an iterator and further work with it.
The best result was shown as expected for with caching the length of the list. Indexing lists does not affect performance since JIT inline it (indexing is a regular property with a parameter).
foreach showed a result about 2.5 times slower than for. This is due to its complex structure that works out behind the scenes (call MoveNext, Current).
List.ForEach as well as Array.ForEach showed the worst result. The fact is that JIT is not inline virtual methods, and as you know, delegates are always called virtually.
The implementation of this method looks like this:
public void ForEach(Action<T> action) { int num = this._version; for (int index = 0; index < this._size && num == this._version; ++index) action(this._items[index]); if (num == this._version) return; ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EnumFailedVersion); }
Here at each iteration, the action delegate is called. It is also checked each time whether the list has changed during the iteration, and if it has changed, an exception is thrown.
An interesting fact is that for arrays the ForEach method is static, while for lists it is instance-based. I believe this is done for the benefit of increased productivity. As you know, the list is based on a regular array, and therefore in the ForEach method, an index is addressed to an array, which is many times faster than an index on an indexer.
Array VS lists

Specific Numbers
- The for loop (without length caching) and foreach for arrays work a little faster than for c with length caching;
- The Array.ForEach loop is about six times slower than the for / foreach loops;
- for (without length caching) on lists is about 3 times slower than on arrays;
- for (with length caching) on lists is about 2 times slower than on arrays;
- foreach on lists is slower than foreach on arrays by about 6 times ;
Prize winners
Among the arrays:

Among the lists are:

In custody
I do not know about you, but for me this article was very interesting. Especially the process of writing it. It turns out that foreach on arrays is faster for-and with length caching. Thanks JIT for optimization. foreach is slower on lists for-this is a fact.
With today's computers, it is impossible to use the foreach loop just because it is slower for-for, probably not entirely correct. After all, the code with its use looks more declarative. Of course, if you need to optimize the code very well, then it’s probably better to prefer for-y.
Thanks for reading.