Aug 3, 2013

Interesting tricks with value types in .Net

A while back I was pairing with a team mate hunting a memory allocation that should not be happing (it was not a leak but it was in a method called thousands of times per second and we do believed it was not required (or at least we should try to avoid it at all costs).

After some code inspection we nailed it down to a foreach loop, something as simple as:

var list = new List<int>();

// the code bellow allocates memory. 
foreach(var item in list)
{
}
Interestingly enough, when compiled against C# 3.5 / 4.0 compilers we could not reproduce this behavior; it was only when compiling on Mono 2.6 (on mono 2.10 it didn't happened).

After some investigation (and head banging) we found the issue; for reasons that I'll not explain here (basically to avoid gc allocations) the enumerator returned by List is a value type but it happens that in order to fulfill the foreach contract it also implements IDisposable. On .Net (at least on 3.5) it happens that the compiler completely avoids boxing this enumerator but when compiled with gmcs (the mono cs compiler) 2.6.5.0 a box gets generated for the code that calls IDisposable.Dispose()! (see the code bellow):

finally
{
     IL_0048: ldloc.2
     IL_0049: box valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator< int32>
     IL_004e: callvirt instance void [mscorlib]System.IDisposable::Dispose()
     IL_0053: endfinally
}
Well, it happens that MS C# compiler (and newer mono ones also) emit different IL for that finally block:
finally
{
     IL_004b: ldloca.s 3
     IL_004d: constrained. valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator< int32>
     IL_0053: callvirt instance void [mscorlib]System.IDisposable::Dispose()
     IL_0058: nop
     IL_0059: endfinally
}
See, no box instruction. Instead of boxing, the compiler loads the value type address and then asks the runtime to handle that address as a reference to IDisposable.

Really interesting (at least IMHO)

Happy codding!

No comments: