Monday, November 29, 2010

Boxing - Bad News

Now then, where did I get to? It's so hard to remember what you said in an old post, ah yes boxing lets you pass value types as object pointers, but sadly there is some bad news.

The performance goodness of using a value type instance goes away when you box it, so if you try that code from the Types post, and then edit it to do some boxing like I suggested in the Good News post you may see some output like:

2147483647 value types took       00:01:00.1181261
2147483647 reference types took 00:01:00.0122760

Ouch! Why did that happen? Rememeber how a value type was good for performance because it didn't carry the overheads associated with instanciating a type on the managaed heap?? When you box a value type, that is exactly what you are doing, infact its slightly less performant because you had to create a value type as well.

So boxing can be evil, is it then, a case of - just make sure you don't box a value type if you don't have to and really really try to avoid boxing inside a loop.

Not quite, as I mentioned previously, boxing is implicit, so you may not even notice that you did it, for example, DateTime is a value type (public struct DateTime) amd it has a property DayOfWeek that returns an enum, also a value type (public enum DayOfWeek). System.Enum provides an override of the System.Object Equals method that will return true if its passed an enum of the same underlying type and value. It could be used like this:

using System;

public sealed class Program {
  public static void Main() {
    DateTime today = DateTime.Now;

    if( today.DayOfWeek.Equals(DayOfWeek.Monday )) {
      Console.WriteLine("It's Monday");
    }
    else {
      Console.WriteLine("It's not Monday");
    }
  }
}

Unsurprisingly, as I was mumbling about implicit boxing, there is some in the code sample above, the culprit is the Equals method call public override bool Equals(Object obj) which wants a parameter of reference type Object. I gave it an instance of a value type instead, so the CLR boxed it for me. How do I know? I checked the Intermediary Language (IL) produced by the compiler using ILDasm:

.method public hidebysig static void Main() cil managed
{
.entrypoint
// Code size 66 (0x42)
.maxstack 2
.locals init (valuetype [mscorlib]System.DateTime V_0,
bool V_1)
IL_0000: nop
IL_0001: call valuetype [mscorlib]System.DateTime [mscorlib]System.DateTime::get_Now()
IL_0006: stloc.0
IL_0007: ldloca.s V_0
IL_0009: call instance valuetype [mscorlib]System.DayOfWeek [mscorlib]System.DateTime::get_DayOfWeek()
IL_000e: box [mscorlib]System.DayOfWeek
IL_0013: ldc.i4.1
IL_0014: box [mscorlib]System.DayOfWeek
IL_0019: callvirt instance bool [mscorlib]System.Object::Equals(object)
IL_001e: ldc.i4.0
IL_001f: ceq
IL_0021: stloc.1
IL_0022: ldloc.1
IL_0023: brtrue.s IL_0034
IL_0025: nop
IL_0026: ldstr "It's Monday"
IL_002b: call void [mscorlib]System.Console::WriteLine(string)
IL_0030: nop
IL_0031: nop
IL_0032: br.s IL_0041
IL_0034: nop
IL_0035: ldstr "It's not Monday"
IL_003a: call void [mscorlib]System.Console::WriteLine(string)
IL_003f: nop
IL_0040: nop
IL_0041: ret
} // end of method Program::Main

"IL_0019: callvirt instance bool [mscorlib]System.Object::Equals(object)" passing in a this pointer.

Which shows two box operations, one to put DayOfWeek.Monday on the managed heap and a second caused by the override of Equals calling into the base types implementation

No comments:

Post a Comment