New* .NET 3.5 Feature: AddIn Framework Resources (Part 2 of 2)
Multi-core CPU Caching: What is volatile?

CLR Fun Stuff: How is my C# code converted into machine instructions?

As we all know that our .NET code (C#, VB.NET etc) are converted into MSIL instructions which in turn are put into the assemblies. MSIL is a high level language, not as low as machine instructions, so it needs to converted into machine specific binary code or needs to be interpreted somehow. Since interpretation would make the execution significantly slower, the code is converted into machine code on access. This is done via a procedure called Just in time compilation (JIT). Just in time is a management concept that was introduced by Ford Motors into production environment. This is a process where inventory was brought on time just before they were needed, and this saved the warehousing or storing costs.

How does JIT compilation happen?

In programming JIT works like this. Since the executable or the library is made of MSIL (bytecode or any other intermediate form) instruction it needs to be compiled into machine code, but if we convert all of the code into machine format then it will take time. For example if JITwe have an application has 50 functions and we use 3 of them regularly, then if each time when we load the program and compile all the 50 functions then it would be a waste of time and would take a long time to load. What JIT does is to convert the function's MSIL into machine code just before executing the function. See the figure to see how a code is compiled via JIT. Once a code has been transformed into native machine code it stays in memory and next calls to function are pointed to that same memory so that the conversion to machine code is done only once in the lifetime of an executable.

A little deeper look: The secret undocumented CLR function that does it all

When a .NET application loads then it loads the MsCorEE.dll which loads the correct version of the MsCorWks.dll (the version of .NET we are running) which contains all core functions of the .NET runtime. For more detail on loading see the web, one good resource can be this post NET Foundations - .NET execution model. There is this function called _CorExeMain which actually loads the CLR and all the types that are required into memory. There is a memory table for types and there are tables for functions and properties of each types.

Lets say we have a class that looks like this

class TestClass
{
   static void ConsoleAdd ( int value1, int value2)
  {
     Console.WriteLine ( value1 + value2);
  }
}

Now a careful look would tell us that only 2 types are used here TestClass and Console. If we call the TestClass.ConsoleAdd function from the main method this is how the memory looks like before the function is called.

jit2

Before the call both the ConsoleAdd and WriteLine and other functions are pointing to the secret JITC function. This is how each time the JITC functions compiles the code into native code and replaces the function pointer in the function table for the type.

            JIT3

Now lets look at the memory after it has been JITed.

JIT3_001

Last words

So now we know how our C# code is compiled in to native code. It may be hard to believe that at certain times JITed code runs faster than the native compiled code. When a code is JIT compiled it takes the advantage of the exact CPU instructions present the the machine but the native compiled code compiles into more generic class of machine instructions. We will read about advantage and disadvantages of JIT another day.

kick it on DotNetKicks.com

Comments

Kazi Manzur Rashid

Shiplu bhai, I have somehow become addicted of your blogs. Keep up the good works rocking.

Chris Pietschmann

Very nice description.

But, you forgot to mention that the framework stores the native compilations in a cache so every time you run the application it doesn't have to recompile it over and over. This gives a performance boost since only the first time the code is run its compiled, and then everytime you execute the app after that it just loads the cached compilation.

There are some exceptions when the framework doesn't save the native compilation, but I'm not sure exactly what they are; I used to know but I've forgotten over the years. Basically, just remember that it does cache some of the JITted code to improve performance when it needs to run that code again.

I guess I'll have to look it up and post my findings...

Egil Hansen

There is also NGen, which is often used during installation of .net programs, to pre-compile the program in to the native language of the platform it is being installed on.

Also, as Chris mentions, the caching helps out a lot as well. I think the exception where caching do not work, is when reflection is used.

Nikola Malovic

First of all, great summary of NET execution model (one more subscriber to your blog here :) )

Egil/Chris,
it is true that JIT compiler optimizes number fo compilations but the compiled mehtods are not stored in any cache, they are stored in High Frequency Loader Heap

Also, as far I am aware every method is been jit compiled once per type/domain

More details on basics of NET memory model can be found here
http://blog.vuscode.com/malovicn/archive/2007/12/30/net-foundations-stack-and-heap.aspx

Shafqat Ahmed

Dear Kazi: Thank you vey much

Chris: If you look at the last diagram you will see that the function table points to the compiled version in memory. It does not point to the JITC function. So it would be cached as long as the compiled memory location remains. Since JITC function is responsible for compiling the MSIL and is no longer pointed by function it gets the performance boost as a reason of the function. I was not much explicit about it in the post.

Egil: Yes, Ngen.exe does CPU specific compilation but it does not take the exact version of the CPU and optimize to those instructions. So first time access to much of the code would be fast in Ngened executable but functions that are repeated many times may perform better in JITed version as JIT compilation will take the advantage of the CPU.

Malovic: Another reader to your posts as well, highly interesting!

Egil Hansen

Shafqat: Ok, it seems odd to me, that ngen would not use the same optimizations as the JIT compiler. Surely, both ngen and the JIT compiler have access to the same information.

Is there a rule of thump about when to use ngen and when not to?

Ohh and by the way, thanks for a interesting post. You have a new reader in me as well.

Shafqat Ahmed

Egil: I have read somewhere in MS blog that from .NET 2.0 the NGen.exe and the JITed code are different. It has been so mainly because, the team wanted less issues to arise from the NGen'ed executable. If you compile to x86 generic model you don't take advantge of the some extra instruction set provided in some processors then still it will support more processors. But with JITed code you can take advantage of those instructions.

Carina

You write very well.

Masudur Rahman

Very nice description.
Thanks

Waleed Eissa

Thanks a lot, that was very informative, looking forward to reading more great posts like this one ...

generic viagra

Ohh and by the way, thanks for a interesting post. You have a new reader in me as well.

generic cialis

I think to not have a woman on the Legislative Council is tantamount to disregarding the views of close to half the population of Brunei Darussalam.

Lisa Alloju

My programming skill is not as good as yours.

The comments to this entry are closed.