Executing dynamic IL with DynamicMethod

One of the features we have in .NET is the ability to execute IL code generated dynamically, in fact there is a complete namespace to generate code at runtime called System.Reflection.Emit

Prior to .NET Framework 2.0 if we wanted to execute the IL code generated we needed to deal with a dynamic assembly making use of classes like the AssemblyBuilder, ModuleBuilder, etc. together with the IL code we can also emit symbolic information making possible to debug the dynamic code even within Visual Studio.

The main downside of this way of generating the IL code is (by design) that the generated code can not be garbage collected and therefore the memory is not released until the AppDomain is unload. This created some controversy about if this was a memory leak or not, which in any case is avoidable by loading everything in a separated AppDomain that can be unloaded when it's not used anymore.

With .NET Framework 2.0 we got the LCG (Lightweight Code Generation), which allows generating code at run time, without having to define the dynamic assembly nor type to contain the methods we create. In addition, the IL and the data structures related to the code generation are allocated on the managed, meaning that they can be garbage collected when there are no more references to the DynamicMethod class that is the main class of LCG.

Below you can see some simple code that dynamically emits the IL for a method "Talk" that concatenates two strings.

   1: class Demo
   2: {
   3:     public void RunDynamicCode()
   4:     {
   5:         MethodInfo concatMethodInfo = typeof(string).GetMethod("Concat", 
   6:             new Type[] { typeof(string), typeof(string) });
   7:  
   8:         DynamicMethod dm = new DynamicMethod("Talk", typeof(string), 
   9:             new Type[] { typeof(string) }, this.GetType(), true);
  10:  
  11:         ILGenerator generator = dm.GetILGenerator();
  12:         generator.Emit(OpCodes.Ldstr, "Hello ");
  13:         generator.Emit(OpCodes.Ldarg_0);
  14:         generator.EmitCall(OpCodes.Call, concatMethodInfo, null);
  15:         generator.Emit(OpCodes.Ret);
  16:  
  17:         Func<string,string> talkDelegate =
  18:             (Func<string, string>)dm.CreateDelegate(typeof(Func<string, string>));
  19:  
  20:         string result = talkDelegate("Jose Bonnin");
  21:  
  22:         Console.Write(result);
  23:         Console.ReadKey();
  24:     }
  25: }

Line 8 instantiates the DynamicMethod and specifies the name, the return type, an array with the parameters received and the type owner. The C# signature would be similar to "string Talk(string);"

Lines from 11 to 15 is where we write the IL code making use of the ILGenerator class. Note that in the line 16 we are emitting a call to the MethodInfo for the method String.Concat we obtained in the line 5.

Finally, in line 17 we do the coolest, we obtain a delegate for the method we have just generated that is callable from our C# code.

The main problem we experience with DynamicMethod is that we do not have the ability to generate debugging info for LCG, since the debugging API is based on metadata that the LCG does not have. In any case, not all is lost since we can continue debugging with WinDBG.

If we attach WinDBG to the code above we can effectively check that we have created a dynamic method and see the IL generated. To do it we just need to obtain a pointer to the DynamicMethod which can be obtained in different ways, an easy one for demo purposes is to the do a !DumpHeap -stat to obtain a list of the different objects grouped by type, then we just need to locate in the list the MT address of the type System.Reflection.Emit.DynamicMethod and run the command !DumpHeap -mt [address] over it. Which will show something similar to this:

0:003> !DumpHeap -mt 6fbf026c
Address      MT         Size
01d0e028   6fbf026c    56
total 1 objects
Statistics:
MT         Count   TotalSize    Class Name
6fbf041c        1            56     System.Reflection.Emit.DynamicMethod
Total 1 objects

Now that we have obtained the address (01d0e028) we can run the command DumpIL to see the dynamic IL generated.

0:003> !DumpIL 01d0e028
This is dynamic IL. Exception info is not reported at this time.
If a token is unresolved, run "!do " on the addr given
in parenthesis. You can also look at the token table yourself, by
running "!DumpArray 01d0e62c".

IL_0000: ldstr 70000002 "Hello "
IL_0005: ldarg.0
IL_0006: call a000003 (01d0e560)
IL_000b: ret

In this post we have seen how we do not need to look to "new" technologies like WPF, Silverlight or WCF to find cool things within .NET

Inline Methods

Method inlining is an optimization technique that consists of putting the body of a method inside the body of all the caller methods and removing the original method. Let's suppose the next sample:

   1: class Demo
   2: {
   3:     public void Talk()
   4:     {
   5:         SayHello();
   6:  
   7:         WaitUserAction();
   8:     }
   9:  
  10:     private void SayHello()
  11:     {
  12:         Console.WriteLine("Hello!");
  13:     }
  14:  
  15:     ...
  16: }

To inline the method SayHello, we should refactor our code in the next way:

   1: class Demo
   2: {
   3:     public void Talk()
   4:     {
   5:         Console.WriteLine("Hello!");
   6:  
   7:         WaitUserAction();
   8:     }
   9:     ...
  10: }

As you can appreciate we have introduced "Console.WriteLine("Hello!");" directly inside the method Talk.

This technique provides some performance benefits by reducing the overhead associated to a new method call. There are many characteristics that make a method a good candidate to be inlined like: reduced size and complexity, methods frequently used, etc. Fortunately this is not something we should think ourselves, most compilers include this optimization when compile our code.

.NET Framework is not an exception and although it does not have an explicit "inline" clause to suggest its use, as you can find in C++, the JIT (Just in Time) Compiler performs inline optimizations when it considers that can help to improve the performance.

If we debug the first version of the code we can appreciate how the method "Talk" and "SayHello" are inlined. The code must be compiled in Release mode and run out of Visual Studio in order the jitter applies the optimizations, so a possible way you can see it is debugging with WinDBG. To do it just run the code and attach the executable, load the Son of Strike extension and run the command !EEStack -EE this will display something similar to:

0015ee28 793e8e33 (MethodDesc 0x79259c00 +0x7  System.Console.ReadKey())
0015ee2c 009100ea (MethodDesc 0x3030a0   +0x32  Demo.WaitUserAction())
0015ee3c 009100a7 (MethodDesc 0x303028   +0x37  Program.Main(System.String[]))

As we see the stack does not display the methods "Talk" and "SayHello" and if we run the command !DumpMT -MD [address of method table] we will see how SOS does not reflect the methods as jitted even if we know the code has been executed. This is because, after inline those methods, its body has been expanded within the Main method but they do "not exist" at all.

79371278   7914b928   PreJIT System.Object.ToString()
7936b3b0   7914b930   PreJIT System.Object.Equals(System.Object)
7936b3d0   7914b948   PreJIT System.Object.GetHashCode()
793624d0   7914b950   PreJIT System.Object.Finalize()
0030c035   00303090   NONE Demo.Talk()
0030c039   00303098   NONE Demo.SayHello()

009100b8   003030a0   JIT Demo.WaitUserAction()
0030c041   003030a8   NONE Demo..ctor()

Just for fun we can change the code and force the jitter to do not inline our methods. This is accomplished adding the attribute [MethodImpl(MethodImplOptions.NoInlining)] on top of the method definition for "Talk" and "SayHello". If we recompile and debug with WinDBG again we will see the differences.

Now the stack shows the calls to the methods "Talk" and "SayHello" and both methods appear as jitted.

0021ed10 793e8e33 (MethodDesc 0x79259c00 +0x7 System.Console.ReadKey())
0021ed14 009a0132 (MethodDesc 0x8830a0 +0x32 Demo.WaitUserAction())
0021ed24 009a00ed (MethodDesc 0x883098 +0x2d Demo.SayHello())
0021ed28 009a00a6 (MethodDesc 0x883090 +0x6 Demo.Talk())

0021ed2c 009a0084 (MethodDesc 0x883028 +0x14 Program.Main(System.String[]))

79371278 7914b928 PreJIT System.Object.ToString()
7936b3b0 7914b930 PreJIT System.Object.Equals(System.Object)
7936b3d0 7914b948 PreJIT System.Object.GetHashCode()
793624d0 7914b950 PreJIT System.Object.Finalize()
009a00a0 00883090 JIT Demo.Talk()
009a00c0 00883098 JIT Demo.SayHello()
009a0100 008830a0 JIT Demo.WaitUserAction()
0088c041 008830a8 NONE Demo..ctor()

You can go one step further checking not only the stacks, but also how the code produced contains the inline instructions. If we use the command !u within WindDBG for each method "Main", "Talk" and "SayHello" when they are not inlined we obtain something similar to this:

Program.Main(System.String[])
Begin 009a0070, size 15
009a0070 b9b0308800 mov ecx,8830B0h (MT: Demo)
009a0075 e8a21f77ff call 0011201c (JitHelp: CORINFO_HELP_NEWSFAST)
009a007a 8bc8 mov ecx,eax
009a007c 3909 cmp dword ptr [ecx],ecx
009a007e ff15e8308800 call dword ptr ds:[8830E8h] (Demo.Talk(), mdToken: 06000003)
009a0084 c3 ret

Demo.Talk()
Begin 009a00a0, size 7
009a00a0 ff15ec308800 call dword ptr ds:[8830ECh] (Demo.SayHello(), mdToken: 06000004)
009a00a6 c3 ret

Demo.SayHello()
Begin 002500c0, size 2e
002500c0 833d8c10f20200 cmp dword ptr ds:[2F2108Ch],0
002500c7 750a jne 002500d3
002500c9 b901000000 mov ecx,1
002500ce e891581179 call mscorlib_ni+0x2a5964 (79365964) (System.Console.InitializeStdOutError(Boolean), mdToken: 06000770)
002500d3 8b0d8c10f202 mov ecx,dword ptr ds:[2F2108Ch] (Object: System.IO.TextWriter+SyncTextWriter)
002500d9 8b153c30f202 mov edx,dword ptr ds:[2F2303Ch] ("Hello!")
002500df 8b01 mov eax,dword ptr [ecx]
002500e1 ff90d8000000 call dword ptr [eax+0D8h]
002500e7 ff15f0301b00 call dword ptr ds:[1B30F0h] (Demo.WaitUserAction(), mdToken: 06000005)
002500ed c3 ret

When the methods are inlined we cannot run anymore the command !u for them, but if we do it for the Main method we can see without being an expert on native code how the code contains now the SayHello instructions.

Program.Main(System.String[])
Begin 00970070, size 38
00970070 b9b0303a00 mov ecx,3A30B0h (MT: Demo)
00970075 e8a21fa2ff call 0039201c (JitHelp: CORINFO_HELP_NEWSFAST)
0097007a 833d8c10c80200 cmp dword ptr ds:[2C8108Ch],0
00970081 750a jne 0097008d
00970083 b901000000 mov ecx,1
00970088 e8d7589f78 call mscorlib_ni+0x2a5964 (79365964) (System. Console.InitializeStdOutError(Boolean), mdToken: 06000770)
0097008d 8b0d8c10c802 mov ecx,dword ptr ds:[2C8108Ch] (Object: System. IO.TextWriter+SyncTextWriter)
00970093 8b153c30c802 mov edx,dword ptr ds:[2C8303Ch] ("Hello!")
00970099 8b01 mov eax,dword ptr [ecx]
0097009b ff90d8000000 call dword ptr [eax+0D8h]
009700a1 ff15f0303a00 call dword ptr ds:[3A30F0h] (Demo.WaitUserAction(), mdToken: 06000005)
009700a7 c3 ret

As you can see there are many things to explore and get fun with WinDBG.

Speakers

Some weeks ago I proposed to the INETA Europe board a set of regulations to become INETA Speaker for the local speakers bureau.

For those who do not know the local bureau, it is a pilot program to provide the user groups with speakers from their own region. The idea born to solve the main problems UG faced when they wanted to request speakers from the European bureau, the problems where mainly related to translation and higher logistical costs implied when somebody comes from different countries.

Today I wanted to write not about those specific rules, but about what makes a good speaker for the .NET User Groups.

If you have participated in any of the events done by companies like Microsoft, Google, Apple, etc. or even the .NET User Groups you have probably seen different styles to communicate the same thing. We have a clear example with Bill Gates and Steve Ballmer and I'm not going to start a new debate to know who is better. But we have many other things that make the speakers different from each other, you can find people who has a great knowledge and people who knows less, speakers who joke a lot others who are very formal, some use a lot of demos to complement the theory...

So, what makes a good speaker ?

I think a good speaker is not the one who has a lot of knowledge and tries to prove it, but the one who is able to communicate the message. Somebody who is not rambling and presents the content clear and easy to understand. A good communicator is able to express and defend ideas well, but not tries to influence people (remember I'm talking about a speaker for .NUGs not a sales man).

All speakers must be well prepared to explain the contents of a session, but must not be afraid of making mistakes or not knowing an answer. I personally hate when somebody tries to answer at any cost. Really, there is no problem if you don't know an answer, you will not be considered a loser because nobody can know everything.

In my opinion those things makes you a good speaker. If you are very formal, you use sense of humor, you wear jacket and tie or T-shirt with casual snickers, that's just your personal style.

Therefore if you think you are a good speaker, stay tuned for  TechEd 2008  and participate in the Speaker Idol contest.

INETA Europe newsletter

Past week I received an e-mail from INETA with the newsletter of July 2008. I must confess I opened the e-mail thinking how pity was that INETA Europe didn't have its own newsletter, but after open it I was positively surprised to see it was not the US newsletter, instead I received the European one.

I'm very glad to see how the Ineta board has decided to start again sending update information on the activities performed, the last newsletter we could see in the website was from September 2006.

I think the newsletters and any kind of direct communication between INETA and it's members is a good way not only to inform the user groups about the activities done, but also to keep the interest of the community in INETA itself. At the end, the INETA work is as important as letting know the NUGs the results of the work in order they can feel there is something in motion. 

The July 2008 publication brings lot of information, you can read the complete newsletter navigating to the link. But I would like to stand up two points:

  • Ineta is looking for volunteers. There are different positions to be covered, if you feel like just send an email to the volunteer coordinator Tomislav Bronzin (tomislav.bronzin@ineta-europe.org).   
  • Ineta Europe gets social. Ineta has just started a new social initiative to share and communicate with the Ineta community: http://community.ineta-europe.org.

 

We all know the community requirements change as the community evolves, therefore it's necessary INETA can evolve together. It's great to see how step by step this is the case.

Blog Change

Welcome to Mad about .NET

I decided to move away from the community http://www.clearscreen.com and start the adventure on my own domain.

For those who do not know me, my name is Jose Bonnin, and I am the Product Development Manager of Payvision, an international payment solutions provider that specializes in multi-currency processing for card-not-present electronic transactions.

Behind my manager role there is a geek who tries to contribute to the .NET community in several ways, I'm Ineta Country Leader and Culminis Country Lead Volunteer for Spain. Therefore, as you probably deducted from the title, the blog subject will not be payment solutions, but the .NET Framework and other related technologies.

Here I want to share my thoughts and opinions regarding this lovely development platform and its community.

I can't finish this first post without thank to the guys of BlogEngine.NET and STUDIO7DESIGNS

I hope you decide bear with me and enjoy reading the posts.

Regards,

Jose.