C# Jit Compiling and .Net

C# JIT compiling and .NET

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.

Why does .net use a JIT compiler instead of just compiling the code once on the target machine?

There are two things to be gained by using an intermediate format like .NET or Java:

  1. You can run the program on any platform, exactly because the code is represented in an intermediate format instead of native code. You just need to write an interpreter for the intermediate format.
  2. It allows for some run-time optimizations which are not (easily) possible at compile-time: for example, you can take advantage of special features on new CPUs, even if those CPUs didn't exist when you wrote your program - only the JIT compiler needs to know about that.

Now, as for why you might not want to perform the compilation on the first run and then just cache that - there can be a couple of reasons for that as well.

If you compile before startup, then the user has to wait a lot longer for that first run - at that point in time, you can't know what the user will actually use. By only compiling what you need, when you need it, you can start much quicker, simply because you have less work to do, and you don't store a lot of code that the user will never use (which for a large program can be a lot of code).

If you start caching the JIT'ted code across sessions, you need to keep track of what has already been compiled and then store it to disk. For a large program, you might have a lot of native code to load from disk. Disk I/O is quite expensive, so it might just take longer to wait for the disk than to re-JIT it. Additionally, you need to keep track of how long that cache is usable. If the hardware changes, you might want to re-JIT in order to apply some new optimizations. If the program changes, you can't use any of your old compiled code. If the run-time compiler changes, then a security bug might have been fixed, and you need to recompile to make sure that bug doesn't remain in your native code.

Basically, the JIT compiler suddenly has a lot more work to do (including disk I/O to deal with the cache) and becomes much more complicated and slow, reducing the point of JIT'ing.

Now, that doesn't mean that it can't sometimes be advantageous to pre-compile certain assemblies, and as Matthew Ferreira points out, that's what the ngen tool can do - but in the general case, it's just not worth doing that, because JIT compilation is often more than fast enough.

JIT compiler and its benefits for speed up the execution of programs in .net in front of c++

Having worked on both C++ compilers and now having spent the past few years working on the .Net JIT, I think there are a few things worth considering:

  • As many others have pointed out, the JIT is running in process with your app, and it tries to carefully balance quick JIT times versus the quality of jitted code. The more elaborate optimizations seen in C++ often come with very high compile time price tags, and there are some pretty sharp knees in the compile-time-vs-code-quality graph.
  • Prejitting seemingly can change this equation somewhat as the jit runs beforehand and could take more time, but prejitting's ability to enlarge optimization scope is quite limited (for instance we try and avoid introducing fragile cross-assembly dependencies, and so for example won't inline across assembly boundaries). So prejitted code tends to run somewhat more slowly than jitted code, and mainly helps application startup times.
  • .Net's default execution model precludes many interprocedural optimizations, because of dynamic class loading, reflection, and the ability of a profiler to update method bodies in a running process. We think, by and large, that the productivity and app architecture gains from these features are worth the trouble. But for cases where these features are not needed we are looking for ways to ensure that if your app doesn't need it, your app won't pay for it.
  • For example we have some "pure" AOT work going on over in CoreRT but as a consequence reflection is limited.
  • .Net Core 2.1 includes a preview of Tiered jitting, which will allow us to ease some of the constraints on jit time -- we'll be able to invest more time jitting methods that we know are frequently executed. So I would expect to see more sophisticated optimizations get added to the JIT over time.
  • .Net Core 2.1 also includes a preview of Hardware Intrinsics so you can take full advantage of the rich instruction sets available on modern hardware.
  • .Net's JIT does not yet get much benefit from profile feedback. This is something we are actively working on changing, though it will take time, and will likely be tied into tiering.
  • The .Net execution model fundamentally alters the way one needs to think about certain compiler optimizations. For instance, from the compiler's standpoint, many operations -- including low level things like field access -- can raise semantically meaningful exceptions (in C++ only calls/throws can cause exceptions). And .Net's GC is precise and relocating which imposes constraints on optimizations in other ways.

The JIT Compiler and event handlers in .NET

Is it the same idea for event handlers?

An event handler is just a delegate, which in turn, will always refer to a method. Since the JIT will compile a method and cache it, the same thing happens for all event handlers, since they're underlying code is just a method.

If you're referring to event handlers defined and written using a lambda expression, ie:

SomeEvent += (o,e) => DoFoo();

Internally, this is still a normal method. The compiler converts the lambda into a normal method in the compiled IL. The same is true for anonymous methods.

Forcing the .NET JIT compiler to generate the most optimized code during application start-up

You can trigger the JIT compiler to compile your entire set of assemblies during your application's initialization routine using the PrepareMethod ... method (without having to use NGen).

This solution is described in more detail here: Forcing JIT Compilation During Runtime.

In C#, on startup, is there a way to force the JIT compiler to touch all MSIL code in the entire app?

As ta.speot.is said, the answer is probably using NGEN; this is a tool the pre-jits your assembly and turns it into native code for the platform you're running on.

Often its run during the setup phase of your application because of this.

MSDN have a guide here:

http://msdn.microsoft.com/en-us/library/6t9t5wcf(v=vs.71).aspx

Get .NET jit-compiler logs

You can use PerfView to get the order in which functions are being JIT-compiled. To be more specific, you can use the JIT Stats which you can generate and access as described in the section "Background JIT in Action" of this article.



Related Topics



Leave a reply



Submit