Grzegorz Sławecki
Grzegorz Sławecki

Reputation: 1797

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

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

Answers (1)

George Polevoy
George Polevoy

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

Related Questions