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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With