Today’s article expands on the previous loop test article to find out which kind of loop is truly fastest. Read on to find out!

For today’s test I started with the previous test and made a few modifications. First, I took Johnny Boy’s suggestion and added a while loop in addition to for and foreach.

Second, I took kzarczynski’s suggestion and made all the loops actually do something with each element of the array or List. Inspired by his articles, I switched from a plain object to a Counter class with a Value field I could access. Each loop now increases a sum local variable by the Value field to force the VM to actually fetch each element.

Third, I moved the List creation and access to after the array and related tests were done. This should help a bit to make sure the CPU cache is primed equally for each set of tests.

Here’s the final result script:

using System;
using System.Collections.Generic;
using UnityEngine;
 
public class TestScript : MonoBehaviour
{
	private const int NumIterations = 100000;
 
	private class Counter
	{
		public int Value;
	}
 
	private string report;
 
	void Start()
	{
		report = "Size,Array For,Array While,Array Foreach,List For,List While,List Foreach\n";
		var sizes = new []{ 10,100,1000 };
		foreach (var size in sizes)
		{
			report += Test(size);
		}
	}
 
	private string Test(int size)
	{
		var stopwatch = new System.Diagnostics.Stopwatch();
		var sum = 0;
		var array = new Counter[size];
		for (int i = 0; i < size; ++i)
		{
			array[i] = new Counter() { Value = i };
		}
 
		stopwatch.Reset();
		stopwatch.Start();
		for (var iteration = 0; iteration < NumIterations; ++iteration)
		{
			for (int i = 0, len = array.Length; i < len; ++i)
			{
				sum += array[i].Value;
			}
		}
		var arrayForTime = stopwatch.ElapsedMilliseconds;
 
		stopwatch.Reset();
		stopwatch.Start();
		for (var iteration = 0; iteration < NumIterations; ++iteration)
		{
			var i = 0;
			var len = array.Length;
			while (i < len)
			{
				sum += array[i].Value;
				++i;
			}
		}
		var arrayWhileTime = stopwatch.ElapsedMilliseconds;
 
		stopwatch.Reset();
		stopwatch.Start();
		for (var iteration = 0; iteration < NumIterations; ++iteration)
		{
			foreach (var cur in array)
			{
				sum += cur.Value;
			}
		}
		var arrayForeachTime = stopwatch.ElapsedMilliseconds;
 
		var list = new List<Counter>(size);
		list.AddRange(array);
 
		stopwatch.Reset();
		stopwatch.Start();
		for (var iteration = 0; iteration < NumIterations; ++iteration)
		{
			for (int i = 0, len = list.Count; i < len; ++i)
			{
				sum += list[i].Value;
			}
		}
		var listForTime = stopwatch.ElapsedMilliseconds;
 
		stopwatch.Reset();
		stopwatch.Start();
		for (var iteration = 0; iteration < NumIterations; ++iteration)
		{
			var i = 0;
			var len = list.Count;
			while (i < len)
			{
				sum += list[i].Value;
				++i;
			}
		}
		var listWhileTime = stopwatch.ElapsedMilliseconds;
 
		stopwatch.Reset();
		stopwatch.Start();
		for (var iteration = 0; iteration < NumIterations; ++iteration)
		{
			foreach (var cur in list)
			{
				sum += cur.Value;
			}
		}
		var listForeachTime = stopwatch.ElapsedMilliseconds;
 
		return size + ","
			+ arrayForTime + ","
			+ arrayWhileTime + ","
			+ arrayForeachTime + ","
			+ listForTime + ","
			+ listWhileTime + ","
			+ listForeachTime + "\n";
	}
 
	void OnGUI()
	{
		GUI.TextArea(new Rect(0, 0, Screen.width, Screen.height), report);
	}
}

If you want to try out the test yourself, simply paste the above code into a TestScript.cs file in your Unity project’s Assets directory and attach it to the main camera game object in a new, empty project. Then build in non-development mode for 64-bit processors and run it windowed at 640×480 with fastest graphics. I ran it that way on this machine:

  • 2.3 Ghz Intel Core i7-3615QM
  • Mac OS X 10.11.4
  • Unity 5.3.4f1, Mac OS X Standalone, x86_64, non-development
  • 640×480, Fastest, Windowed

And here are the results I got:

Size Array For Array While Array Foreach List For List While List Foreach
10 3 2 2 3 3 15
100 25 25 26 33 36 88
1000 255 253 276 301 336 864

Loop Performance (Array 10)

Loop Performance (Array 100)

Loop Performance (Array 1000)

Loop Performance (List 10)

Loop Performance (List 100)

Loop Performance (List 1000)

The results have changed quite a bit since last time! I suspect this is largely due to the switch to using the elements, but it’s tough to know for sure. In any case, the array tests now show almost no difference between the three loop types for 100 and 1000 elements. The for loop is still strangely slower than the others for 10 element arrays, but the numbers are so small that it’s possible this is due to other reasons such as that test running first on a colder CPU cache.

The List tests have changed a little, too. Rather than foreach being massively faster than for, it’s a much closer race. The foreach loop is still fastest regardless of List length, but not tremendously so. Still, beware the garbage creation that will take place when using foreach on a List. That garbage will need to be collected eventually and you’ll pay the cost at that point.

The overall takeaway here is that loop type doesn’t really matter for arrays. For List, use for or while to avoid garbage creation. It’s unlikely that the speed penalty will matter much but the eventual GC hit will be killer.

What’s your preferred loop for arrays and lists? Let me know in the comments!