Tag Archives: Msil

Improving code performance

Recently, an old co-worker contacted me to ask me a question about code performance. Specifically, he was emitting IL from his code and had some questions about some of the opcode usage he witnessed when viewing the IL of some compiled assemblies.  The question was based on a simple application he wrote in C#, compiled, and disassembled.  He did this to see how the C# compiler produced IL and give him clues in how he should emit IL.  The function in question was as follows:


public object GetProp(string name)
{   if (name == “X”
   {    return this.X;
   }
   return null;
}

Now, the code obviously isn’t meant to do anything other than lend some insight into the IL.  Compiling to ‘debug’ the following IL was produced.

.method public hidebysig instance object
        GetProp(string name) cil managed
{
// Code size       35 (0x23)
.maxstack  2
.locals init ([0] object CS$1$0000,
          [1] bool CS$4$0001)
IL_0000:  nop
IL_0001:  ldarg.1
IL_0002:  ldstr      “X”
IL_0007:  call       bool [mscorlib]System.String::op_Equality(string, string)
IL_000c:  ldc.i4.0
IL_000d:  ceq
IL_000f:  stloc.1
IL_0010:  ldloc.1
IL_0011:  brtrue.s   IL_001d
IL_0013:  nop
IL_0014:  ldarg.0
IL_0015:  call       instance string class TestApp.TClass`1::get_X()
IL_001a:  stloc.0
IL_001b:  br.s       IL_0021
IL_001d:  ldnull
IL_001e:  stloc.0
IL_001f:  br.s       IL_0021
IL_0021:  ldloc.0
IL_0022:  ret
} // end of method TClass`1::GetProp

The question was, why was the stloc.1 and ldloc.1 needed after the ceq instruction at IL_000d (there are actually other issues in this small snippet, but I’ll focus on this particular one) . I, too, tried to resolve the issue and batted a few guesses around.  I proffered two ideas, and then ultimately suggested that the JIT compiler would likely be modifying this code anyway (particularly once it was recompiled in ‘release’ with optimization).

Still curious as to why the compiler produced the stloc and ldloc opcodes, I asked around internally until Vance set me straight with this blog post.

Introduction: What does ‘foreach’ actually do?
http://blogs.msdn.com/vancem/archive/2006/02/20/535807.aspx


Essentially, he states what I initially felt — that the JIT transformations on the IL are so dramatic, that you cannot judge an application’s performance based on the IL.  He also gives some great information on how to view your JITed code — with release optimizations and everything.  The other side to this is, that after further review, the inefficiencies of the IL were fixed in the optimized IL anyway once the code was set to ‘release’. 

Sometimes, it’s really easy to get side-tracked by these discussions in your quest for software glory.  I’m glad to know we have people like Vance around to set me straight when I do.


Delegates in C# vs VB.NET down to the IL

So I found a post on a blog the other day from one of my friends. He is a recent convert from VB.NET to C# and he asked where his event handlers were. Anyone that has been coding in both VB.NET and C# knows exactly what the problem is. Many people tend to misunderstand where the .NET runtime ends and language syntax begins. Let’s remember that both languages must compile to Intermediate Language (IL). The way each language chooses to implement .NET’s features in sytax is totally up to the language designer. This post will attempt to show the differences in these languages and break down how this all works behind the scenes. I’ll show you how events are hooked up in VB.NET, then in C#. I’ll show you how these are compiled down to IL and then provide references where you can find more information if you really want it.

First off, lets look at how we implement event handlers for controls. When I double click on a control in a VB.NET Windows Forms project, the development environment drops me into VB.NET code and a newly created event handler. The event handler is hooked up as follows:

Private Sub Button1_Click(ByVal sender As System.Object, _
  ByVal e As System.EventArgs) Handles Button1.Click
End Sub

VB.NET knows that this event (Button1_Click) is handling the Click event of the Button1 object by the very aptly named “Handles” keyword followed by the dotted notation of Object.EventName. This, of course, follows the EventHandler delegate method signature. When I compile this code, the VB.NET compiler outputs the following IL:

IL_0037:  ldarg.0
IL_0038:  dup
IL_0039:  ldvirtftn
          instance void VBHandlers.Form1::Button1_Click(object,
             class [mscorlib]System.EventArgs)
IL_003f:  newobj
          instance void [mscorlib]System.EventHandler::.ctor(object,
             native int)
IL_0044:  callvirt
          instance void [System.Windows.Forms]System.Windows.Forms.Control::add_Click(
             class [mscorlib]System.EventHandler)

This IL is fiarly simple, the ldvirtftn field is pushing the pointer to the Button1_Click function’s implementation onto the stack. This first pushes the Button1 object onto the stack. The object instance is then popped from the stack and the address of the entry point to Button1_Click is retreived. That pointer is then pushed onto the stack. Next the event handler is created with the newobj call, and finally, the callvirt method adds the handler to the Click event. Notice that the click event implements an add_Click syntax. This is because there is a little known feature of .NET events which provide event accessors. Much like you can use Get and Set accessors for properties, you can implement add and remove accessors for events. You might also recognize the prefixed operation on the event is similar to the way that operator overloading looks in IL (i.e. op_Equal).

In C# we have the same convenience of double clicking a control to quickly implement the default event for that control. When we double click the same button in a C# Windows Forms project, we are aslo presented in a C# code window that has the following code:

private void button1_Click(object sender, System.EventArgs e) {
}

You can see that we have our method implemented that matches the delegate method signature, but where is our handler? How does the button know to hook this up? What happens if I rename my button? Does it just know to handle this method when the event is raised based on the method name? Not hardly. The development environment also adds a handler to this method, but it does it in a more object oriented manner. That is, it hooks up the event by an assignment operator. If you look at your C# windows forms code again, look for the “Windows Form Designer generated code” code region and expand it. You’ll notice in the InitializeComponent method, there is a section of this method dedicated to setting the values for the button1 button instance. At the end of this section is code similar to the following:

this.button1.Click += new System.EventHandler(this.button1_Click);

This code tells the event to append an EventHandler to the Click event. That event handler just happens to be our button1_Click method. When we compile our C# code, we get nearly the same IL output:

IL_005a:  ldarg.0
IL_005b:  ldftn
              instance void CSharpHandlers.Form1::button1_Click(object,
             class [mscorlib]System.EventArgs)
IL_0061:  newobj
              instance void [mscorlib]System.EventHandler::.ctor(object,
              native int)
IL_0066:  callvirt
              instance void [System.Windows.Forms]System.Windows.Forms.Control::add_Click(
              class [mscorlib]System.EventHandler)

Did you catch the difference between the C# and VB.NET IL output? We are no longer calling ldvirtftn, but instead, we are simply calling ldftn. This is the difference between VB.NET using a virtual dispatch sequence and C# using an instance dispatch sequence. Both are valid according to Ecma-335 standards. My guess is that if you don’t understand delegates, you won’t get the difference between these two fields. The main thrust of what most need to see is that both languages implement the same functionaility differently through their language syntax. It’s up to the compiler to interpret the syntax and output the appropriate IL code — which more often than not is nearly identical regardless of what language you use. For some languages it may make more sense to use a different IL construct within the same category, but the output is essentially the same. If you attempt to use a different .NET compatible language and you don’t see a feature that you were expecting to see, don’t give up. The feature is most likely implemented in a different way.