C# JIT compiling and .NET
Asked Answered
C

5

96

I've become a bit confused about the details of how the JIT compiler works. I know that C# compiles down to IL. The first time it is run it is JIT'd. Does this involve it getting translated into native code? Is the .NET runtime (as a Virtual Machine?) interact with the JIT'd code? I know this is naive, but I've really confused myself. My impression has always been that the assemblies are not interpreted by the .NET Runtime but I don't understand the details of the interaction.

Colyer answered 8/4, 2011 at 1:17 Comment(0)
P
93

Yes, JIT'ing IL code involves translating the IL into native machine instructions.

Yes, the .NET runtime interacts with the JIT'ed native machine code, in the sense that the runtime owns the memory blocks occupied by the native machine code, the runtime calls into the native machine code, etc.

You are correct that the .NET runtime does not interpret the IL code in your assemblies.

What happens is when execution reaches a function or code block (like, an else clause of an if block) that has not yet been JIT compiled into native machine code, the JIT'r is invoked to compile that block of IL into native machine code. When that's done, program execution enters the freshly emitted machine code to execute it's program logic. If while executing that native machine code execution reaches a function call to a function that has not yet been compiled to machine code, the JIT'r is invoked to compile that function "just in time". And so on.

The JIT'r doesn't necessarily compile all the logic of a function body into machine code at once. If the function has if statements, the statement blocks of the if or else clauses may not be JIT compiled until execution actually passes through that block. Code paths that have not executed remain in IL form until they do execute.

The compiled native machine code is kept in memory so that it can be used again the next time that section of code executes. The second time you call a function it will run faster than the first time you call it because no JIT step is necessary the second time around.

In desktop .NET, the native machine code is kept in memory for the lifetime of the appdomain. In .NET CF, the native machine code may be thrown away if the application is running low on memory. It will be JIT compiled again from the original IL code the next time execution passes through that code.

Platon answered 8/4, 2011 at 2:2 Comment(4)
Minor pedantic correction - 'The JIT'r doesn't necessarily compile all the logic of a function body' - In a theoretical implementation that may be the case, but in the existing implementations of the .NET runtime, the JIT compiler will compile whole methods all at once.Gomar
As to the why this is done, it is primarily because a non-jit compiler cannot assume that certain optimizations are available on any given target platform. The only options are to either compile to the lowest common denominator, or more commonly, to compile multiple versions, each targeted at it's own platform. JIT eliminates that disadvantage because the final translation to machine code is done on the target machine, where the compiler knows what optimizations are available.Planter
Chris, even thought JIT knows what optimizations are available, it has not time to apply any significant optimizations. Probably NGEN is more powerfull.Readytowear
@Readytowear Yes, NGEN has the luxury of time that the JIT compiler does not, but there are plenty of codegen decisions that the JIT can make to improve the performance of the compiled code that don't take a lot of time to figure out. For example, the JIT compiler can select instruction sets to best match the available hardware found at runtime without adding significant time to the JIT compile step. I don't think the .NET JITter does this in any significant fashion, but it is possible.Platon
G
29

Code is "compiled" into the Microsoft Intermediate Language, which is similar to assembly format.

When you double-click an executable, Windows loads mscoree.dll which then sets up the CLR environment and starts your program's code. The JIT compiler starts reading the MSIL code in your program and dynamically compiles the code into x86 instructions, which the CPU can execute.

Galileo answered 8/4, 2011 at 1:22 Comment(0)
E
3

I will describe compilling IL code into native CPU instructions via example below.

public class Example 
{
    static void Main() 
    {
        Console.WriteLine("Hey IL!!!");
    }
}

Primarily CLR knows every details about type and what method being called from that type this is due to metadata .

When CLR starting to execute IL into native CPU instruction, that time CLR allocate internal data structures for every type referenced by Main's code.

In our case we have only one type Console, thus CLR will allocate one internal data structure. Via that internal structure, we will manage access to the referenced types.

Inside that data structure, CLR has entries about all methods defined by that type. Each entry holds the address where the method’s implementation can be found.

When initializing this structure, CLR sets each entry in undocumented FUNCTION contained inside in CLR itself. And as you can guess, this FUNCTION is what we call JIT Compiler.

Overall you could consider JIT Compiler as a CLR function, which compilling IL into native CPU instructions. Let me show you in details how this process will be in our example.

1.When Main's make his first call to WriteLine, the JITCompiler function is called.

2.JIT Compiler function knows what method is being called and what type defines this method.

3.Then Jit Compiler searches the assembly where defined that type and get IL code for the method defined by that type in our case IL code of WriteLine method.

4.JIT compiler allocate DYNAMIC memory block, after that JIT verify and compile IL code into native CPU code and save that CPU code in that memory block.

5.Then JIT compiler goes back to the internal data structure entry, and replaces the address(which primarily reference to the IL code implementation of WriteLine) with address new dynamically created memory block, which contain native CPU instructions of WriteLine.

6.Finally, the JIT Compiler function jumps to the code in the memory block and execute native code of writeline method.

7.After execution of the WriteLine, code returns to the Mains'code which continues execution as normal.

Envelop answered 15/6, 2017 at 20:9 Comment(0)
G
1

.NET uses an intermediate language called MSIL, sometimes abbreviated as IL. The compiler reads your source code and produces MSIL. When you run the program, the .NET Just In Time (JIT) compiler reads your MSIL code and produces an executable application in memory. You won't see any of this happen, but it's a good idea to know what's going on behind the scenes.

Gardell answered 31/8, 2016 at 10:33 Comment(0)
E
0

.NET Framework uses CLR Environment to produce the MSIL(Microsoft Intermediate Language), also called as IL. The compiler reads your source code and when you build/compile your project, It generates MSIL. Now, when you finally run your project, The .NET JIT(Just-in-time Compiler) comes in action. JIT reads your MSIL code and produces native code(which are x86 instructions) which can be easily executed by the CPU.JIT reads all the MSIL instructions and executes them Line by Line.

If you're interested to see, what happens behind the scenes , it has been already answered. Please follow - Here

Effeminate answered 4/12, 2018 at 10:38 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.