Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Foreach on collection cast to IEnumerable work slower than without cast?

Tags:

c#

linq

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.

like image 623
Grzegorz Sławecki Avatar asked Oct 08 '15 12:10

Grzegorz Sławecki


1 Answers

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.

like image 87
George Polevoy Avatar answered Oct 01 '22 04:10

George Polevoy