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.