For vs Foreach Performance
A comment on a recent post of mine on Stack Overflow suggested that I replace my "for" loop with a more "modern" version using "foreach" and Enumerable.Range. In other words:
// replace this for (int i = 0; i < 1000; i++) { } // with this foreach (var i in Enumerable.Range(0, 1000)) { }
I can't really say that I prefer one over the other, although the second approach does look kinda cool. It'd be even nicer if there were some native support for ranges in C# like there was in Pascal:
// borrowing Pascal's range syntax foreach (var i in [0..1000]) { }
... but I digress. What I wanted to talk about in this post is my findings on the performance difference between a simple "for" loop and a "foreach" over Enumerable.Range. I timed two long loops using a Stopwatch:
const int count = 100000000; var sw = new Stopwatch(); sw.Reset(); sw.Start(); for (int i = 0; i < count; i++) { } sw.Stop(); var forTime = sw.ElapsedTicks; sw.Reset(); sw.Start(); foreach (var i in Enumerable.Range(0, count)) { } sw.Stop(); var foreachTime = sw.ElapsedTicks; Console.WriteLine(forTime); Console.WriteLine(foreachTime); Console.WriteLine((float)foreachTime / forTime);
The result:
4869915
14286932
2.933713
So the Enumerable.Range approach is three times slower than a simple for loop.
I guess in a real-world situation where the body of the loop is the thing taking the bulk of the time, there wouldn't be that big a difference, but it's interesting that the two approaches differ in performance by so much. Something to be aware of if you're very performance-oriented in your code, anyway.
# Trackback from For vs Foreach Performance (in .NET) on 29/10/2008 9:11 AM
# Trackback from Weekly Link Post 66 « Rhonda Tipton’s WebLog on 3/11/2008 10:37 AM
# Trackback from For vs Foreach Performance : Mad Props! – Matt Hamilton | Uni Ego Blog on 13/01/2010 6:41 AM
Comments
# Jarrod Dixon
28/10/2008 11:52 AM
More proof that they'll have to pry my for loop from my cold, dead fingers!
Very nice post!
# mabster
28/10/2008 12:00 PM
I'm still not sold on the for loop's syntax (it reeks of its C roots) but there's no debating the numbers. I won't be changing any existing code, that's for sure!
Thanks for the comment, Jarrod! Great to have one of the Stack Overflow Illuminati drop by!
# Amit
28/10/2008 7:03 PM
I think the sample code that you chose to investigate the performance of the two kinds of loop was not exactly proper, though the results that you arrived at were perfectly logical.
I doubt anyone would use a foreach loop to iterate through a simple data type like int or double or float etc. Most of the times, its used to iterate through a collection, with each item in the collection holding an object.
A more proper test would have been to take a collection of objects (even strings) and run it using a for loop and a foreach loop and then check the performance.
My guess would be that a for-loop would still be faster than a foreach loop since in a foreach loop, at each iteration, it has to invoke a method of a class which is being used as the iterator whereas in the for-loop , the language runtime is simply going forward in stack space to access the next value of the iterator variable.
A very good thought-provoking article nevertheless.
# mabster
28/10/2008 7:08 PM
Hi Amit,
Realistically I should've called this post "For vs Enumerable.Range", because that's what it's really testing. I won't change it now, but your point is taken.
# rzei
29/10/2008 1:37 AM
Doesn't this equal as microbenchmarking, or did you turn off the optimization some how?
One could argue that the way Enumerable.Range is being implemented is to allocate an Enumerable<Integer> instance to provide those ints for you until it hits the limit.
While doing so, JVM allocates memory, calls methods through interfaces... Allowing the time for JVM to optimize this case -- I think it would be clear that the actual code would decimate in to the actual for(;;;) implementation, perhaps without the cost of actually ever reserving any memory for the Enumerable<Integer>.
The point that should be made, is that does that clarify your source code enough (make it non-programmer readable for code reviews) to justify that once/twice/n-times-off slight penalty?
IFF you'd actually do something in while in the loop, the price of Enumerable.range(...) might quickly vanish.
# rzei
29/10/2008 1:39 AM
My bad, this is .NET :D Ok, then I guess you should do early micro-optimization.
# mabster
29/10/2008 6:25 AM
Hi rzei,
I won't argue that this code is hardly realistic. It came about from someone asking for a nice way to time some code using .NET's Stopwatch class, so in this case it made sense to find the loop that would take as little time as possible.
In a real-world situation of course you'd use whichever syntax was more readable/maintainable.
Thanks for your comments!
# mavell
8/12/2008 11:26 AM
Good post.. Thanks! :D
# Igor
19/12/2008 7:30 AM
I tried your code on a relase build, and the ratio gets worse:
nd\bin>Debug\LinqPlayGround.exe
For Time 11,689,223.00
For Each Time 42,906,260.00
Ratio: 3.67
nd\bin>Release\LinqPlayGround.exe
For Time 1,485,365.00
For Each Time 32,706,204.00
Ratio: 22.02
# Sekhar Aripaka
4/05/2011 1:44 PM
ForEach works efficiently for Objects not for primitive data types like int.
I have tested this with 1 million objects and see ForEach is running 20-25% faster than For...
Run this code and see for yourself...
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ForVsForEach
{
class Program
{
static void Main(string[] args)
{
EmployeeCollection<Employee> ec = new EmployeeCollection<Employee>();
int maxItems = 1000000;
System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();
Console.WriteLine("Loading employee objects...");
sw.Start();
for (int i = 0; i < maxItems; i++)
{
ec.Add(new Employee(i.ToString(),i));
}
sw.Stop();
Console.WriteLine(string.Format("Done loading {0} employee objects. Time lapsed {1} Milliseconds.", maxItems.ToString(), sw.Elapsed.TotalMilliseconds.ToString()));
Console.ReadLine();
Console.WriteLine("Reading employee objects using for() loop...");
sw.Restart();
for (int i = 0; i < maxItems; i++)
{
string name = ec.Get(i).Name;
}
sw.Stop();
Console.WriteLine(string.Format("Done reading {0} employee objects using for() loop. Time lapsed {1} Milliseconds.", maxItems.ToString(), sw.Elapsed.TotalMilliseconds.ToString()));
Console.ReadLine();
Console.WriteLine("Reading employee objects using ForEach() loop...");
sw.Restart();
foreach (Employee e in ec.Items)
{
string name = e.Name;
}
sw.Stop();
Console.WriteLine(string.Format("Done reading {0} employee objects using ForEach() loop. Time lapsed {1} Milliseconds.", maxItems.ToString(), sw.Elapsed.TotalMilliseconds.ToString()));
Console.ReadLine();
ec.Clear();
}
}
class Employee
{
public Employee(string name,int salary)
{
this.Name = name;
this.Salary = salary;
}
public string Name { get; set; }
public int Salary{ get; set; }
}
public class EmployeeCollection<T>
{
private List<T> ar = new List<T>();
public T Get(int pos)
{ return ar[pos]; }
public void Add(T c)
{ ar.Add(c); }
public void Clear()
{ ar.Clear(); }
public int Count
{ get { return ar.Count; } }
public List<T> Items
{ get { return ar; } }
}
}
# JOhnny Boy
20/05/2016 4:19 PM
While your comparisons are great, here's a blog comparison that iterates over multiple kinds of objects, such as DataRows and custom objects, also including the performance of the While loop construct: cc.davelozinski.com/.../for-vs-foreach-
At the minimum, it would be great to see your future articles incorporate the while loop too as a comparison.