16

Java has been designed to run on a virtual machine to allow portability for programs.

However .NET has been designed from the start specifically for Windows.

Than what is the reason for .NET applications being compiled to bytecode for the CLR?

Was it simply to copy Java? Or is there a technical advantage to just compiling natively?

Aviv Cohn
  • 21,190
  • 31
  • 118
  • 178
  • 24
    You are confused, "Windows" is not a platform which implies one and only one specific processor architecture. Moreover, your premise is wrong. – Doc Brown Sep 28 '15 at 08:24
  • 1
    see also: http://www.developer.am/c-net-platform/?page=the-platform-independent-nature-of-net – Doc Brown Sep 28 '15 at 11:01
  • 5
    You can even [run windows 10 on the Raspberry Pi](http://www.engadget.com/2015/09/27/microsoft-windows-10-raspberry-pi-starter-kit/). –  Sep 28 '15 at 13:27
  • 15
    If you rephrase your initial statement to "Java uses a virtual machine architecture to **lower the costs** of creating portable programs", does your question about the CLR answer itself? – Eric Lippert Sep 28 '15 at 13:41
  • @MichaelT good luck running Word on it though :-) – gbjbaanb Sep 28 '15 at 14:21
  • @gbjbaanb Word is not written in .NET, so... there has been a .NET framework that runs on Windows CE for years which is older embedded devices, and on the newer phone style runtime it runs .NET as well. The Windows 10 on rPi will run .NET just fine, though a slimmer framework will be available - the whole *runtime* will be available. These are of course two different things, one a set of libraries, the other the actual JIT and executing VM you reference here. So that VM has been ported quite a bit already. – Jimmy Hoffa Sep 28 '15 at 14:25
  • 2
    @gbjbaanb heh. Its more intended for The Internet Of Things as the underlying operating system (rather than Linux). I don't foresee it being used to run Word on my refrigerator, or thermostat. –  Sep 28 '15 at 14:26
  • 8
    But, I want to run Sharepoint on my toaster :( – MetaFight Sep 28 '15 at 14:28
  • 2
    @MetaFight [as it turns out](http://superuser.com/questions/792607/why-does-windows-think-that-my-wireless-keyboard-is-a-toaster) you can run toaster as an input device, if it helps. – bunyaCloven Sep 28 '15 at 15:16
  • 1
    Eric Lippert wrote a really good blog post on this, http://blogs.msdn.com/b/ericlippert/archive/2011/11/18/why-il.aspx – asawyer Sep 28 '15 at 16:04
  • Well, Windows has an ARM version as well. Maybe they planned it to run on other plataforms? – Ismael Miguel Sep 28 '15 at 17:19
  • 2
    ...see also: [What is the advantage of a programmers VM apart from portability](http://programmers.stackexchange.com/questions/216107/what-is-the-advantage-of-a-programmers-vm-apart-from-portability) – gnat Sep 28 '15 at 21:35
  • @IsmaelMiguel it was already available on MIPS, PowerPC, DEC Alpha, Itanium... long before it appeared on ARM – phuclv Sep 29 '15 at 06:10
  • @LưuVĩnhPhúc Forgot Itanium, the others I had no idea about. I really didn't knew that Windows could run on PowerPC – Ismael Miguel Sep 29 '15 at 07:11
  • 1
    @IsmaelMiguel: When Windows NT was designed (back when it was still intended to be the next version of OS/2), it was not at all clear that x86 would dominate the PC market. So, Dave Cutler designed it to be highly portable. He even went so far as to use development machines with an extremely strange processor architecture (the Intel i860) that was unlike any other mainstream CPU. This, BTW, is also where the OS got its name: Intel hadn't decided on a marketing name for the CPU yet, its codename was N10. The OS was just an experiment, it didn't have a name either, so the engineers referred to … – Jörg W Mittag Sep 29 '15 at 07:41
  • 1
    … it as "N10 OS/2", pronounced "Enn-Ten Oh Ess Two", often shortened to "NT OS/2". IBM and Microsoft split, because IBM deemed the design too aggressive and too far removed from OS/2 v1, so IBM developed its own version of OS/2 v2. Dave Cutler, fortunately, had not only designed NT OS/2 to be highly portable, but also to support different OS personalities (originally, OS/2 and Unix/POSIX), so it was easy to slap on Win16, Win32, and DOS personalities as well (fun fact: until Windows 2000, you could still run OS/2 programs unmodified, because the OS/2 personality was still shipped). – Jörg W Mittag Sep 29 '15 at 07:44
  • @JörgWMittag That's something that I was only lightly aware. But that one is a really nice piece of history. Sadly, Windows isn't that compatible now. – Ismael Miguel Sep 29 '15 at 08:18
  • @IsmaelMiguel: Well, it runs on x86, AMD64, IA64, and ARM (and let's not forget the XBox 360 running an XP-based OS on PowerPC), and it runs Win32, Win64, and DOS. The POSIX personality also still exists, I believe, but it is shipped as a separate install. Also, a couple of years ago, I met an IBM engineer who told me that they regularly acquire the source releases from Microsoft and build them on POWER, just in case. He even alluded to, but didn't publicly admit, that they had a PS3 running Windows XP. (IBM designed the CPUs for both PS3 and XBox 360 based on a common core.) – Jörg W Mittag Sep 29 '15 at 08:26
  • @JörgWMittag Windows XP on a PS3? Sounds fun! But Isn't XBOX OS NT-based? (I know that GameCube was Windows-based as well) – Ismael Miguel Sep 29 '15 at 08:37
  • When you consider that there are a number of .NET languages, it would make sense for them all to share a common form underneath the hood. – Robbie Dee Oct 01 '15 at 11:28

4 Answers4

38

Compiling to some bytecode is an old tradition. UCSD P-code existed in 1978, and had many precursors. Today, LLVM can be seen as a bytecode, targetted by Clang/LLVM ahead-of-time compiler suite and GCCJIT can be viewed as a JIT related to GCC (with GIMPLE sort-of being some internal bytecode).

(hence, bytecode, JIT, ... has quite fuzzy meanings today; JIT's broadest sense is compilation inside the process running the compiled code.)

The JVM bytecode was initially implemented as interpreter. But Java become popular enough to get JIT based JVMs (and Sun invested a lot in JIT technology, so this helped Java to become successful).

And JIT existed long time ago (in the early 1980s, e.g. in Lisp machines, and even in 1960 on the CAB 500 computer and others), before even the name was used. Many Common Lisp or Smalltalk implementations had JIT compilers (and today, SBCL is fully JIT-ing).

In my understanding, Microsoft designed the CLR bytecode to be JIT compiled (hence got different tradeoffs in its bytecode than the JVM). And it has recently published its implementation as open-source software and ported it to Linux (before that, Mono existed on Linux).

A bytecode is often more compact than native binary executables, it can be made portable to several architectures (e.g. x86 32 bits and x86-64 and also ARM 32 bits, ARM/Aarch64, ...) and might be designed to avoid (or at least soften) dependency hells.

A big advantage of JIT compilation is that the VM can recompile some parts of the bytecode based upon dynamic contextual information (e.g. profiling, call stack introspection, ...) some code. Some JIT-ing infrastructures like libjit, asmjit, LLVM, GCCJIT, ... don't do that (however, the implementation using them could do that by repeated use of the JIT-ing infrastructure), but most industrial JVM or CLR implementations do it (and some people call JIT only that lazy on-demand dynamic compilation; for me JIT is just a buzzword for dynamic compilation at runtime). This is difficult or impossible with AOT compilation (at least, requires LTO), and is impossible if you want to do profile-guided optimization dynamically at runtime (as most JVM or CLR JIT implementations are rumored doing). Also, a bytecode VM don't need to JIT-compile all the bytecode, but only the most used parts (as HotSpot does) and keep interpreting the rarely used cold code.

Also JIT implementations can cooperate much more (and better...) with sophisticated garbage collectors.

PS. I know nothing about Windows. I never used it. I'm using Linux since 1994 and Unix since 1987.

Basile Starynkevitch
  • 32,434
  • 6
  • 84
  • 125
  • 2
    It's interesting to note that even in the 1960s, the Apollo guidance computer used a somewhat similar scheme. See [here on NASA's History site](http://history.nasa.gov/computers/Ch2-6.html), the section titled "Programming the AGC". I recall seeing something more thorough on Wikipedia but can't seem to find it. – user Sep 28 '15 at 13:35
  • 1
    Without going as far as x86_64 to ARM, each stepping in the x86 or x86_64 families introduced new features (e.g. vector instructions) that you would have to ignore for the sake of retrocompatibility when distributing a statically compiled executable. A JIT machine can take advantage of those, it's like having `-march=native` always on. – Stefano Sanfilippo Sep 28 '15 at 14:18
  • *A big advantage of JIT compilation is that the VM can recompile parts* => while very difficult to impossible with native code, simple delivering LLVM IR and generating binaries from that would be possible. Actually, the Mill CPU creators plan on doing something similar for their CPU: write target-agnostic code, and a specializer on the machine generates machine-specific native code (with all the available instructions). It's still AOT, since you generate before starting execution. – Matthieu M. Sep 28 '15 at 14:40
  • @MatthieuM. you are describing an LLVM VM there. (its even in the name) – Caleth Sep 28 '15 at 15:13
  • @Caleth: No. Although such a VM could be useful, of course, and I've toyed with the idea of creating such. However here I am *just* suggesting delaying the "final" pass (ie, running the back-end part of LLVM): it would still produce a native binary that could be executed straight away... it would just produce it "in-situ" rather than on another machine. – Matthieu M. Sep 28 '15 at 15:17
  • 2
    I'm not seeing how this differs from a VM in anything other than eagerness of bytecode->native translation. But then I would label x86 as a VM nowadays, with numerous hardware implementations from Intel and AMD – Caleth Sep 28 '15 at 15:32
  • I haven't heard about PGO being used in a CLR JIT. There is [mpgo](https://msdn.microsoft.com/en-us/library/hh873180%28v=vs.110%29.aspx), but that's specifically for AOT compilation. – svick Sep 28 '15 at 17:21
  • Lisp has mostly not been using JIT. All modern Common Lisp implementations are AOT compilers. Lisp Machines all used AOT compilers. SBCL is strictly AOT. – Rainer Joswig Sep 28 '15 at 20:46
  • 1
    @MatthieuM. I've never heard of the Mill CPU, but the [Transmeta architecture](https://en.wikipedia.org/wiki/Transmeta#Products_and_Customers) took a very similar approach, with VLIW instructions although they use dynamic compilation and caching instead of AOT. The few Transmeta x86 laptops appeared on the market achieved higher performance with very low power compared to that time's Intel CPUs – phuclv Sep 29 '15 at 06:28
  • @RainerJoswig: It depends upon your definition of JIT. I feel that `libjit`, `asmjit`, `LLVM`, `GCCJIT` are JIT implementations, but you probably don't. – Basile Starynkevitch Sep 29 '15 at 07:39
  • What definition would make SBCL being JIT-based, given that everything gets compiled to machine code AOT? – Rainer Joswig Sep 29 '15 at 07:42
  • JIT meaning for me dynamically compiling to code inside the same process running that code (and this is the meaning in libjit, asmjit, LLVM, GCCJIT, so I am not the only one understanding that). Then obviously SBCL is JIT-ing – Basile Starynkevitch Sep 29 '15 at 07:46
  • If you start an SBCL application, it does not need to compile anything at start or on demand when a routine gets called. SBCL does not do any adaptive optimization such as dynamic recompilation. SBCL for some time did not even have the capability to run non-compiled code. There is also now no general mechanism for the interpreter to decide to compile code. Compiled code can't be modified, it can only replaced or extended. Though there are some Lisp systems, which did compile code under runtime control - usually not as a general mechanism - for example to create effective methods. – Rainer Joswig Sep 29 '15 at 18:58
  • If the application calls `eval` a machine code is generated... Hence the JIT compiler is embedded in most applications in Lisp compiled by SBCL. – Basile Starynkevitch Sep 29 '15 at 19:03
  • There is simply nothing 'just in time' in SBCL. There is no mechanism in SBCL which determines when it is time to compile something. Calling every runtime compiler a 'JIT' defeats the purpose of 'just in time'. Suddenly zillions of historic runtime compilers would be relabeled as a JIT. – Rainer Joswig Sep 29 '15 at 19:04
  • EVAL calls a compiler for new source code, but not a JIT compiler. It's an explicit call by the application and not under runtime control. There is nothing 'just in time' - it's even AOT. The code is fully compiled before it is executed. It's a simple embedded runtime compiler. The compiler called by EVAL also has no special runtime information available. – Rainer Joswig Sep 29 '15 at 19:09
23

Windows is not a single, homogenous platform. When the first version of the clr was released, it targeted not only the traditional windows 98 family of systems (which only ran on x86) but also windows nt 4 (x86, ppc, alpha, mips). Support for windows ce (which ran on x86, sh, arm, mips and ppc) was added in version 1.1, while ia64 ("itanium") and x86-64 targets on nt were added in version 2. It is quite likely that the developers knew that most or all of these platforms would need to be supported when the project began. In fact, it seems probable that the fact that ISVs weren't keen to support this many hardware platforms and tended to only release x86 versions of applications figured in microsoft's decision to go ahead with developing the system.

Jules
  • 17,614
  • 2
  • 33
  • 63
  • 1
    An interesting aside: initial Windows NT development [specifically targetted non-IA-32 platforms](https://en.wikipedia.org/wiki/Windows_NT#Development). – user Sep 28 '15 at 13:24
  • @MichaelKjörling I worked at SGI at the time when you could run Windows NT on MIPS systems in a supported configuration by SGI itself. I recall Windows NT on Alpha systems prior to graduating from the university too (back in '95). –  Sep 28 '15 at 13:25
  • @MichaelT The NT 4 installation CD had binaries for a number of architectures. I believe Alpha was one of them. – user Sep 28 '15 at 13:37
  • 4
    Not only was it possible, but the DEC Alpha machines were the fastest NT machines around. – MSalters Sep 28 '15 at 15:10
9

Eric Lippert has a good explanation of why the .NET languages target IL instead of directly generating binary files titled Why IL?.

The reason is that the cost/effort of developing your compiler is cheaper/simpler with this approach. Your high level language compiler generates a common intermediate language. This makes it easy to add new languages as there is only one platform to generate code for. Then you have your 2nd stage compilers that take this intermediate language and output the platform specific binary (OS and CPU architecture).

This way, each time you add support for a new platform (such as a new processor), you just need to write a single compiler that compiles IL to the new platform.

Glorfindel
  • 3,137
  • 6
  • 25
  • 33
Eric Johnson
  • 444
  • 2
  • 5
2

Many features of the .NET type system, especially the generic types which were added in 2.0 but (from what I understand) anticipated from the get-go, make it literally impossible to compile code for all of the types a program might use before it starts execution (since the combinations of type used by a program may be affected in arbitrary ways by the inputs received by a program, and although the number of discrete types actually used by a program in the course of a single execution must be bounded, the number of types that a program may use in response to various inputs need not be). While dynamic code generation is possible even without a virtual machine, using a virtual machine makes it much easier and safer.

Further, the efficiency of garbage collection in a multi-threaded environment can be improved tremendously by giving the garbage collector the ability to simultaneously block all threads that might make any use of references to garbage-collected objects. Even if the garbage collector is required to keep "stop-the-world" events as short as possible, the ability of the collector to unilaterally lock out other threads from interacting with GC references makes it possible for other threads to read and write references without having to use interlocked reads and writes. This can have enormous effects on efficiency.

Most of what the .NET VM manages to accomplish could be done without using a VM, but using a VM offers huge safety and performance benefits which far outweigh the downsides.

supercat
  • 8,335
  • 22
  • 28