Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

CLR vs JIT

What is the difference between the JIT compiler and CLR? If you compile your code to il and CLR runs that code then what is the JIT doing? How has JIT compilation changed with the addition of generics to the CLR?

like image 839
Ted Smith Avatar asked Mar 02 '09 11:03

Ted Smith


People also ask

Is JIT faster than compiled?

A JIT compiler can be faster because the machine code is being generated on the exact machine that it will also execute on. This means that the JIT has the best possible information available to it to emit optimized code.

What is the difference between JIT and compiler?

A compiler compiles (translates) the given program to executable code (whole code at a time). A JIT compiler performs a similar task but it is used by JVM internally, to translate the hotspots in the byte code. A compiler compiles (translates) the given program to executable code (whole code at a time).

What is the difference between JIT and interpreter?

Interpreter is a program that translates the programmer written instructions or scripts into corresponding machine code that matches a particular hardware platform of a CPU. On the other hand, JIT is a compiler that translates bytecodes into machine codes at runtime. It requires CPU time and memory.

What does JIT do during compilation process?

The JIT compiler helps improve the performance of Java programs by compiling bytecodes into native machine code at run time. The JIT compiler is enabled by default. When a method has been compiled, the JVM calls the compiled code of that method directly instead of interpreting it.


2 Answers

The JIT is one aspect of the CLR.

Specifically it is the part responsible for changing CIL (hereafter called IL) produced by the original language's compiler (csc.exe for Microsoft c# for example) into machine code native to the current processor (and architecture that it exposes in the current process, for example 32/64bit). If the assembly in question was ngen'd then the the JIT process is completely unnecessary and the CLR will run this code just fine without it.

Before a method is used which has not yet been converted from the intermediate representation it is the JIT's responsibility to convert it.
Exactly when the JIT will kick in is implementation specific, and subject to change. However the CLR design mandates that the JIT happens before the relevant code executes, JVMs in contrast would be free to interpret the code for a while while a separate thread creates a machine code representation.
The 'normal' CLR uses a pre-JIT stub approach where by methods are JIT compiled only as they are used. This involves having the initial native method stub be an indirection to instruct the JIT to compile the method then modify the original call to skip past the initial stub. The current compact edition instead compiles all methods on a type when it is loaded.

To address the addition of Generics.

This was the last major change to the IL specification and JIT in terms of its semantics as opposed to its internal implementation details.

Several new IL instructions were added, and more meta data options were provided for instrumenting types and members. Constraints were added at the IL level as well.

When the JIT compiles a method which has generic arguments (either explicitly or implicitly through the containing class) it may set up different code paths (machine code instructions) for each type used. In practice the JIT uses a shared implementation for all reference types since variables for these will exhibit the same semantics and occupy the same space (IntPtr.Size).

Each value type will get specific code generated for it, dealing with the reduced / increased size of the variables on the stack/heap is a major reason for this. Also by emitting the constrained opcode before method calls many invocations on non reference types need not box the value to call the method (this optimization is used in non generic cases as well). This also allows the default<T> behaviour to be correctly handled and for comparisons to null to be stripped out as no ops (always false) when a non Nullable value type is used.

If an attempt is made at runtime to create an instance of a generic type via reflection then the type parameters will be validated by the runtime to ensure they pass any constraints. This does not directly affect the JIT unless this is used within the type system (unlikely though possible).

like image 31
ShuggyCoUk Avatar answered Sep 29 '22 12:09

ShuggyCoUk


You compile your code to IL which gets executed and compiled to machine code during runtime, this is what's called JIT.

Edit, to flesh out the answer some more (still overly simplified):

When you compile your C# code in visual studio it gets turned into IL that the CLR understands, the IL is the same for all languages running on top of the CLR (which is what enables the .NET runtime to use several languages and inter-op between them easily).

During runtime the IL is interpreted into machine code (which is specific to the architecture you're on) and then it's executed. This process is called Just In Time compilation or JIT for short. Only the IL that is needed is transformed into machine code (and only once, it's "cached" once it's compiled into machinecode), just in time before it's executed, hence the name JIT.

This is what it would look like for C#

C# Code > C# Compiler > IL > .NET Runtime > JIT Compiler > Machinecode > Execution

And this is what it would look like for VB

VB Code > VB Compiler > IL > .NET Runtime > JIT Compiler > Machinecode > Execution

And as you can see only the two first steps are unique to each language, and everything after it's been turned into IL is the same which is, as I said before, the reason you can run several different languages on top of .NET

like image 145
thr Avatar answered Sep 29 '22 13:09

thr