24

The title pretty much sums it up but I was wondering why systems like .net compile code every time it is run instead of just compiling it once on the target machine?

Atli
  • 243
  • 2
  • 4
  • Most of the answers didn't read your question carefully. He's asking: why can't the target machine do the compilation once and keep it (not: why can't we compile once and distribute the binary) – egrunin Oct 01 '10 at 20:41
  • 1
    It already works that way. Look through the services installed on your machine. The "Microsoft .NET Framework NGEN" service springs into action to pre-compile assemblies. If properly tickled by an installer. – Hans Passant Oct 01 '10 at 20:44
  • 2
    It doesn't compile every time it is run. When a method is JIT compiled, the generated native code is stored in the native image cache. On subsequent runs if the runtime sees a method has already been compiled it doesn't compile it again. – mhenry1384 Apr 05 '11 at 14:19

6 Answers6

29

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.

Michael Madsen
  • 54,231
  • 8
  • 72
  • 83
  • 1
    #1 is likely the single most important reason – STW Oct 01 '10 at 20:12
  • 5
    Neither of these points are relevant to the question: atli asked about compiling it on the target machine, not the developer's machine. – JasonFruit Oct 01 '10 at 20:15
  • Yes, that's much better. -1 removed – JasonFruit Oct 01 '10 at 20:44
  • People seldom upgrade their hardware, so it's probably a good idea to run the JIT when running the application's installer for faster load time. But that creates the issue of how to check if the .Net framework has been updated and the machine code should be recompiled by the JIT. – Gulbahar Jul 05 '12 at 09:53
6

I don't write compilers or run-times, so I can't say this for certain, but I don't believe just-in-time = every time. It just means we wait until we really need to compile, but once the compile is done for that instance, it won't be compiled again.

cdkMoose
  • 1,646
  • 19
  • 21
  • 1
    You are correct: if a piece of code is not executed during a session, then it doesn't need to be JIT compiled. However, the JIT compiled code is not (usually) cached for future sessions, so the JIT compilation happens again the next time you run the program. – Michael Madsen Oct 01 '10 at 20:14
  • @Michael That is incorrect. NGen caches JIT compiled code for future reuse. – J D Feb 11 '18 at 09:16
5

What if you moved the executable to another machine? If it was compiled once for the first machine's specific processor, it would not be able to take advantage of a (possibly) more advanced or efficient instruction set available on the new processor. If you really want to compile once on the target machine, consider using ngen.exe during your application's installation.

3

Another advantage of JIT compilation: code that was written 10 or 15 years ago can get a significant performance boost by simply being run with a state-of-the art JIT, without any changes to the code at all.

Michael Borgwardt
  • 342,105
  • 78
  • 482
  • 720
  • But the CLR could easily include a version number in the hypothetical disk-cached JITted version of the assembly and delete it the first time it is run on an updated CLR. – binki Jan 22 '18 at 01:51
2

The title pretty much sums it up but I was wondering why systems like .net compile code every time it is run instead of just compiling it once on the target machine?

.NET does cache the results of compiling CIL to native code on a given target but this is not trivial:

  • There is a trade-off between loading cached data from disk and recompiling on-the-fly.
  • Any form of run-time code generation requires the ability to compile on-the-fly. This is not only the use of System.Reflection.Emit but also reification of types from dynamically-loaded assemblies and even the creation of new value types during polymorphic recursion.
J D
  • 48,105
  • 13
  • 171
  • 274
  • The fact that `Expression.Compile()` exists (and whatever else is vaguely eval-like) actually probably is one of the biggest reasons to have a very good JIT. – binki Jan 22 '18 at 01:54
1

If you compile directly to machine-code then you've targeted a specific system:

Code --> Machine Code

However, in .NET, Java, etc. what you are targeting is the virtual machine. This produces code which any .NET compliant VM can interpret, and in turn JIT into real machine code.

So, in .NET:

Code --> IL --> VM (executing) --> JIT'd Machine Code

Initially it looks more complicated, but consider targeting multiple architectures:

Code(x86) --> Machine Code(x86)
Code(ppc) --> Machine Code(ppc)
etc... (1x code-set per target)

Versus

Code --> IL --> VM(x86) (executing) --> JIT'd x86 Machine Code
                VM(ppc) (executing) --> JIT'd ppc Machine Code
                etc...

My illustration is rough, but you'll note that a single package of .NET code can target and run on multiple platforms

Michael Petrotta
  • 59,888
  • 27
  • 145
  • 179
STW
  • 44,917
  • 17
  • 105
  • 161