Performance Iterating Generic Lists
C#, C# Language October 30th, 2006There are 3 obvious ways of iterating through each item in a generic list, but which is the most efficient.
- using a for statement
- using a foreach statement
- using the List.ForEach method with a delegate
I created the following simple application to test the differences:
{
List<ListItem> items = new List<ListItem>();
ProfileTimer timer = new ProfileTimer();
public Form1()
{
InitializeComponent();
}
private void buttonAllocate_Click(object sender, EventArgs e)
{
int itemCount = (int)numericUpDown1.Value;
textBox1.Clear();
textBox1.AppendText("Allocating " + itemCount.ToString() + " items.\n");
items.Capacity = itemCount;
timer.Start();
for (int i = 0; i < itemCount; i++)
{
items.Add(new ListItem());
}
timer.End();
textBox1.AppendText("Took:" + timer.TimeTaken().ToString() + "\n");
}
private void buttonForLoop_Click(object sender, EventArgs e)
{
textBox1.AppendText("----------------------------------------------\n");
textBox1.AppendText("Iterating with for loop\n");
timer.Start();
int itemCount = items.Count;
for (int i = 0; i < itemCount; i++)
{
items[i].Value++;
}
timer.End();
textBox1.AppendText("Took:" + timer.TimeTaken().ToString() + "\n");
}
private void ButtonForEachStatement_Click(object sender, EventArgs e)
{
textBox1.AppendText("----------------------------------------------\n");
textBox1.AppendText("Iterating with foreach\n");
timer.Start();
foreach(ListItem item in items)
{
item.Value++;
}
timer.End();
textBox1.AppendText("Took:" + timer.TimeTaken().ToString() + "\n");
}
private void buttonForEachDelegate_Click(object sender, EventArgs e)
{
textBox1.AppendText("----------------------------------------------\n");
textBox1.AppendText("Iterating with foreach delegate\n");
timer.Start();
items.ForEach(delegate(ListItem item)
{
item.Value++;
}
);
timer.End();
textBox1.AppendText("Took:" + timer.TimeTaken().ToString() + "\n");
}
}
Where the list item is defined as
class ListItem
{
public int Value;
}
The following results were obtained in Seconds
| No of items in the list | For Statement | ForEach Statement | ForEach Delegate |
| 100,000 | 0.000909729 | 0.00154351 | 0.001170701 |
| 1,000,000 | 0.009031616 | 0.015998993 | 0.011646201 |
| 10,000,000 | 0.093305468 | 0.160015975 | 0.114651431 |
Tests run on an Intel Pentium Dual Core 3.2GHz with 3Gb Ram.
As we can see the choice of iterator makes very little difference when there are a small amount of items in the list, but as we move upto iterating lists containing hundreds of thousands of items, there are some performance improvements between the methods.
The For statement out performs the rest, 71% faster than the ForEach Statement, but it should be noted that the itemcount check in the for loop needs to be stored and not checked on each iteration. i.e. don’t use for(int i=0; i<items.count;i++) If the items.count is called on every iteration the perfromace is the same as the ForEach Delegate.
The ForEach delegate comes in second, 39% faster than the ForEach Statement.
Conclusion
For most applications there is not much performance impact in the iteration times, but if time is critical or you have nested iterations then you should be using a basic for loop (BUT rememeber to store the limit in an integer and use in the comparison and DONT allocate a new ListItem variable in the iterator).
Personally, having come from a delphi background and tending to use the iterator pattern, I’ll be using the ForEach delegate. I think it reads nicer and gets a reasonable performance.
January 21st, 2007 at 7:25 pm
Interesting. Never would have believed there would be a difference at all - more thinking that the same MSIL code would be generated after compilation and optimization. However agreed - ForEach delegate is the most elegant - “regular ForEach” is what I still use (too much .C# 1.1 coding) and performance should be the last priority - and only applied if/when you do notice a performance problem.
January 25th, 2007 at 11:09 am
Nice!, This is a good practical example for judging the performance of Iterating lists. Often, I used ForEach. This is essential when you are seriously taking the performance in account.
May 9th, 2008 at 7:19 pm
Where is located ProfileTimer class?
You might be missing timer.Reset() . Without that, ForEach Statement, ForEach Delegate are stacking times on top of each other.
May 12th, 2008 at 8:26 am
The profilerTimer is my own class that simply gets a hires tickcount and stores it on start(), then gets another tickcount and stores it on end(). Time taken is end - start.
I’ll see if i can dig out the code.
June 23rd, 2008 at 5:10 am
This is really good example to compare for, foreach.
Thanks for sharing.