I’ve noticed a performance hit of iterating over a primitive collection (T[]) that has been cast to a generic interface collection (IList or IEnumberable).
For example:
private static int Sum(int[] array)
{
int sum = 0;
foreach (int i in array)
sum += i;
return sum;
}
The above code executes significantly faster than the code below, where the parameter is changed to type IList (or IEnumerable):
private static int Sum(IList<int> array)
{
int sum = 0;
foreach (int i in array)
sum += i;
return sum;
}
The performance hit still occurs if the object passed is a primitive array, and if I try changing the loop to a for loop instead of a foreach loop.
I can get around the performance hit by coding it like such:
private static int Sum(IList<int> array)
{
int sum = 0;
if( array is int[] )
foreach (int i in (int[])array)
sum += i;
else
foreach (int i in array)
sum += i;
return sum;
}
Is there a more elegant way of solving this issue? Thank you for your time.
Edit: My benchmark code:
static void Main(string[] args)
{
int[] values = Enumerable.Range(0, 10000000).ToArray<int>();
Stopwatch sw = new Stopwatch();
sw.Start();
Sum(values);
//Sum((IList<int>)values);
sw.Stop();
Console.WriteLine("Elasped: {0} ms", sw.ElapsedMilliseconds);
Console.Read();
}
Your best bet is to create overload for
Sumwithint[]as argument if this method is performance-critical. CLR’s JIT can detect foreach-style iteration over array and can skip range checking and address each element directly. Each iteration of loop takes 3-5 instructions on x86, with only one memory lookup.When using IList, JIT does not have knowledge about underlying collection’s structure and ends up using
IEnumerator<int>. Each iteration of loop uses two interface invocation – one forCurrent, one forMoveNext(2-3 memory lookups and a call for each of those). This most likely ends up with ~20 quite expensive instructions and there is not much you can do about it.Edit: If you are curious about actual machine code emitted by JIT from Release build without debugger attached, here it is.
Array version
IEnumerable version