Reputation: 1797
Today I found something that I don't quite understand. I got the following code in LinqPad (version 5):
void Main()
{
const int size = 5000000;
List<Thing> things = Enumerable.Range(1, 5000000).Select(x => new Thing {Id = x}).ToList();
var sw1 = Stopwatch.StartNew();
foreach (var t in things)
if(t.Id == size) break;
sw1.ElapsedMilliseconds.Dump();
var sw2 = Stopwatch.StartNew();
IEnumerable<Thing> ienThings = things;
foreach (var t in ienThings)
if (t.Id == size) break;
sw2.ElapsedMilliseconds.Dump();
}
class Thing
{
public long Id { get; set; }
}
It appears that second loop takes twice as long as the first one. Why would this simple cast cause such an effect? I'm sure that there's something simple happening under the hood that I am somehow missing.
Upvotes: 8
Views: 391
Reputation: 7681
This is due to the difference between the call
and callvirt
instruction used.
call System.Collections.Generic.List<UserQuery+Thing>+Enumerator.get_Current
call System.Collections.Generic.List<UserQuery+Thing>+Enumerator.MoveNext
vs
callvirt System.Collections.Generic.IEnumerator<UserQuery+Thing>.get_Current
callvirt System.Collections.IEnumerator.MoveNext
The callvirt
instruction does a null check, that is why it is slower.
Upvotes: 7