5

This is a detailed answer about how C# compiler optimizes foreach in the cases when IEnumerator<T> is a mutable struct.

Does F# compiler perform the same optimization?

Community
  • 1
  • 1
V.B.
  • 6,236
  • 1
  • 33
  • 56

1 Answers1

6

AFAICT It seems F# treats valuetype enumerators in a similar manner as C#:

Here is a disassembled snippet of IL code from a simple C# program that uses foreach over an IEnumerable<T>

.locals init (
    [0] valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<int32>,
    [1] int32 v
)
IL_0025: ldloca.s 0
IL_0027: call instance bool valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<int32>::MoveNext()

Note that local 0 is a valuetype and that it uses ldloca.s to load the address of the struct.

Compare it with F#

.locals init (
    [0] class [mscorlib]System.Collections.Generic.List`1<int32> ra,
    [1] valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<int32>
)
IL_000e: ldloca.s 1
IL_0010: call instance bool valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<int32>::MoveNext()

This code also uses valuetype to declare local 1 and ldloca.s to load the address of the struct.

As a side note: Later F# version does actually do an optimization that C# don't do. Because it's a common pattern in F# to iterate over F# lists that are immutable data structures it's ineffective to iterate using enumerators. So F# has a special case for lists and applies a more efficient algorithm in that case. In C# iterating over F# lists would fallback to enumerators.

It would be possible to implement a special handling for IList types as well but since it's possible someone has implement IList in a "funny" way it's a potentially breaking change to implement such an optimization.