This is a weird collection of observations about C# and CLR intermingled without apparent rhyme or reason, although the author seems to understand that they are not interchangeable...
> It also bleeds into differences between C# and Java whose features often include compiler-level-only features that the underlying VirtualMachine is not cognizant of.
aka "if the list says C#, its because it applies to C# and not CLR"
it still confuses the issue as the title says it's a CLR vs JVM comparison, there are dozens of languages on each so i don't see why they're bringing C# vs java into it.
CLR .tail prefix is only a hint and not guaranteed. And traditionally it's had a ton of restrictions and limitations and was slow enough that F# stopped emitting it except when needed. (A lot of code ends up tail call optimizable, even if not recursive or designed that way.)
Is rather have the JVM's compressed pointers and escape analysis.
Though it's important to note that it's only a suggestion for the CLR runtime. For instance, in .NET v2 (which as an engine also covers v3) it was rare for tail call optimization to trigger in 64-bit mode. They finally fixed this in .NET v4 to make F# viable.
Well since .tail is only a hint, F# must do it in some cases. Try writing a simple recursive function in tail call style and look at the IL produced by fsc.
I was basing my statement on this line from the MSDN blog I linked:
> This is such an important concept that the JIT has a special path just for turning functions that call themselves in a tail recursive manner into loops.
However, it looks like (in at least some cases) that fsc unrolls the recursion prior to the JIT:
let rec fact n acc =
match n with
| 0 -> acc
| _ -> fact (n-1) (acc*n)
.method assembly static int32 fact@1(int32 n,
int32 acc) cil managed
{
// Code size 24 (0x18)
.maxstack 5
IL_0000: ldarg.0
IL_0001: switch (
IL_0016)
IL_000a: ldarg.0
IL_000b: ldc.i4.1
IL_000c: sub
IL_000d: ldarg.1
IL_000e: ldarg.0
IL_000f: mul
IL_0010: starg.s acc
IL_0012: starg.s n
IL_0014: br.s IL_0000
IL_0016: ldarg.1
IL_0017: ret
} // end of method TailTest::fact@1
I understand JVM is battle tested on Linux, *BSD, where CLR would have to catch up, but are there any technical reasons say JVM perfs better than CLR on windows.
This multitiered approach does not necessarily make the end result faster. It is in vogue to do this, and for startup performance having a baseline JIT can be beneficial, but for workload throughput adding another tier isn't going to do much unless you have some concrete optimizations you are going to run that you decided against due to cost of compile time.
> ... for workload throughput adding another tier isn't going to do much unless you have some concrete optimizations you are going to run that you decided against due to cost of compile time.
Isn't this statement pretty much the entirety of the reason to implement multi-tiered JITs? Do a fast JIT with few optimizations to get out of interpreted mode. Then do a slow JIT after you've collected enough statistics to make good optimization decisions.
To me that's like saying compression algorithms don't necessarily make data smaller. It's true enough, but you can still make general comparisons between approaches.
If your work has an "inner-loop" situation, then the JVM can optimize the heck out of it using statistics specific to this particular execution. That's huge.. it opens up all kinds of concrete optimizations that aren't possible with static analysis alone (precomputing call targets, knowing what to inline, etc.). I'd wager this is a major factor in cases when the JVM occasionally outperforms C.
Actual gathered statistics say you're wrong. Profile guided optimisations can easily add 20% performance or more. And for that to work, the easiest path for the developer is a tiered JIT. More concretely, you can disable the tiered JIT on the JVM and find out what it buys you (and there are people who have done this).
The fact is that before the JVM has a chance to optimize the code, the app already restarted by the user unless it is some long running server process. On the server side, JVM is so heavy, normally there is only 1-2 heavy JVM running for web server and database driver on the entire machine. On the client side, Java is a joke. Though users now have more choices: Swing, AWT, JavaFx. Every one of these is hard to use comparing with C# or Qt. Most native platform developers hate Java, including OSX developers.
> but are there any technical reasons say JVM perfs better than CLR on windows
Considering the amount of man-hours that have gone into both, the exact reasons are probably hidden under a whole bunch of implementation details.
That being said, I've run benchmarks where the JVM (version 1.8) outperforms C++ and Fortran... The JVM seemingly optimizes everything, and the amount of fine tuning you can do is amazing.
I've never heard of the CLR coming close to Java performance on large-ish apps or tasks, all else being equal.
There are fast, optimised Java apps as well. IntelliJ is actually a good example - on my laptop it starts in a few seconds, the UI is snappy and responsive, it looks native. Back when I used Eclipse I thought the same as you; Java apps must just inherently suck. The difference in performance between IntelliJ and Eclipse put that idea to bed for good.
The real reason Java apps are often associated with being big and heavy is less to do with the technology and more to do with the fact that it's so often used for bespoke enterprise apps where there's no competition and so little incentive to optimise (vs add new features).
I agree that IntelliJ is OK comparing with other Java apps. It is never near performant. Visual Studio being 32 bit app is still much faster than 64 bit IntelliJ, given that IntelliJ can use much more memory. Every time, people complain about IntelliJ, they are told that they will need better hardware. The reason Enterprises chose Java, is because they don't care about performance that much.
Visual Studio being 32 bit is an embarrassment for Microsoft in this day and age. That's exactly the kind of technical hole that JetBrains avoided when they bet on Java to write their IDEs in. The transition for them from 32 bit to 64 bit was extremely smooth despite having a huge plugin ecosystem. I've read about the bizarre hacks people with big Visual Studio projects have to use to ensure they can actually load them, that's just not sustainable.
I haven't compared the speed of a recent VS vs IntelliJ as I use a Mac now (another benefit JB got from going with Java). But of my complaints about IntelliJ none are really related to speed.
BTW if it's typing responsiveness you're thinking of, I recall that JB had some inefficient drawing algorithms which they've optimised in very recent builds (or it might even be an experimental feature still). It could cause issues on slow graphics cards. That isn't a Java issue though, it was just IJ using inefficient graphics code.
I have used latest edition of IntelliJ on OsX. IntelliJ is acceptable but not smooth at all considering that I have 16g ram with 1tb Ssd. And font rendering due to the jvm swing bug is hurting eyes. It is far fetched to say the experience is any pleasant. To fix the issue of Java, jetbrain bundles their custom built jdk
I use IntelliJ on the same setup and don't see any font issues. I haven't noticed any lack of smoothness either, but perhaps we have different standards for that.
Another important performance issue is related to Java's handling about generic type erasure and auto boxing. This is one of the biggest design issue comparing with C#'s handling. So manipulating bytes is a pain. And there is even no unsigned byte. Benchmark does not represent real world use case. I don't feel Java is much faster than Python or Ruby. But there is too much boilerplate code comparing with C#. The pure Java language is clean. But with Spring framework and various Apache common, endless different logging framework, it makes the language such a hassle to deal with. Just my 2 cents.
JVM doesn't really have a notion of virtual versus non-virtual methods, it's a matter of which calling opcode you use (invokevirtual versus invokespecial).
It's also sort of missing the big difference... the JVM is generally superior in performance to the CLR, having a better JIT and better GC performance.
I'm not sure I'd consider performance a big difference. Their performance is generally very close, and if you're in a situation to care enough about performance to split hairs then you really shouldn't be using a managed platform in the first place.
The _real_ big difference is that Java has an enormous open source ecosystem, whereas much of what .NET has is ports or clones of successful Java projects.
That said, I think .NET's probably the better choice for a lot of line-of-business applications simply because the tooling allows for very rapid development of that sort of stuff, and not having easy access to Hadoop or Akka really isn't much of a handicap when you're mostly just banging out a mess of business rules.
The title implies that it's comparing the runtime engines, and the performance is absolutely a major selling point of any such comparison, even if the practical effect of any difference is negligible. Ultimately, the comparison claims to be between the CLR and the JVM while it's more like between C# and Java, given how it only covers fairly superficial features at the front-end level and not what can and can't be done in VM code, let alone how different VM architectures can have major impacts on code design, etc.
I think the reason for this particular list is that it's from the standpoint of a programming language implementation team, and one that's currently working on targeting a 2nd platform.
Given that, it reads as a list of what the VM can do for you and what you'll need to do yourself in the compiler.
Which is funny/sad, as the CLR has more opportunity for perf. For instance, in its generics implementation and value types.
Though the CLR has a lot more "outs", where you can use raw pointers and such. Even put " managed" objects on an unmanaged heap. Don't think this is possible in the JVM. So you can write more C-like code in .net then you can represent in the JVM.
But both speculative optimisation/specialisation and escape analysis can recover some of the benefits of those features, can they not?
And I don't know if it is possible yet, but for Java 9 Graal is an extension onto of the JVM and can run unmanaged C code alongside normal code, so it should be possible.
This is not "differences between CLR and JVM", but more of "why I think CLR is better than JVM".
Anyway the biggest issue I have seen in my line of work (building GPU software, and people want GPU acceleration from their language of choice) is that JVM does not provide a way to handle external resources properly. Calling garbage collection does not guarantee that the out of scope objects will be finalized. This results in either code that uses up a lot of memory or code that is very un-java like.
Yes I know try with resources exist, but the problem still exists for any temporary variables you create within the block.
If anyone here has a better solution, I am all ears.
I suggest never to call the garbage collector with the expectation that anything in particular will happen--not in CLR and not in the JVM. Also, do not use finalizers for this!
JNI and P/Invoke are not really that different except that P/Invoke is a lot easier to implement. But CLR does not know how to cleanup unmanaged resources automagically.
edit: I removed my notes about "try with resource" and AutoCloseable
The trick is to write a managed wrapper that takes care of the housekeeping with AutoCloseable / IDisposable, exception flow control, etc. Careful use of arrays, nonboxed types, and statics instead of beating up the heap space... it can be tedious but someone's got to do the dirty work.
This is quite difficult and puts you more into the realm of DSL's. Have you tried adding a fluent Builder interface? It's one of the better ways to handle these types of problems in Java. For example:
Otherwise if you're willing to do Lombok style AST manipulation you could of course do most anything you wanted, perhaps even proper operator overloading:
```
@Arrays
public void test() {
Array C = A + sin(B);
}
```
(Just be ready for the Java programmers to rebel on you and your "dark arts" ;) )
Having recently been tinkering with the Objective-C Runtime (writing a bridge to another language), I think it'd be interesting to extend the comparison there as well. Actually, a more systemic review of what all the various various runtime platforms are capable of, apart from any particular languages implemented on those platforms, would be excellent reading.
(For just one example, I'd really like to see a comparison of the availability+overhead of various concurrency primitives in the runtimes of the JVM vs. Erlang's BEAM, vs. Obj-C's libdispatch, etc. The languages on each platform emphasize concurrency differently, but often at the VM level things are more similar than people think.)
There's nothing stopping you from declaring additional static classes inside a class in Java. You can definitely declare multiple classes in a single file.
That's not even close to the same thing. C# allows declaration of types within other types, but it also allows declaration of multiple top-level types within a single compilation unit. And with partial classes, it also allows declaration of a single type across multiple compilation units.
It's a small thing, but between this and the properties syntax sugar (avoiding having to hand-write all the SetX GetX methods that litter Java code), it adds up for cruddy stuff where you end up needing to define a bunch of POCOs, for instance with EF or JSON REST APIs.
The point that was made is that multiple (static) classes cannot be declared in the same file, which is 100% false. I'm not arguing that C# is functionally equivalent to Java. There was no mention of "compilation unit" or "partial classes", which are a completely different subject.
It doesn't matter whether the additional classes defined in the file are top level or not, they're still classes that can be used independently.
You are being disingenuous. "File" is layman-speak for "compilation unit", so it is not a "completely different subject". You cannot declare multiple top-level types in a single compilation unit in Java. Yes, you can create static types within another type. However, those are not top-level types; they are scoped within the type they are declared in.
Let's just put it this way. Would a Java programmer, by way of normal operation, implement a project with a few Java files, each of which contains a type with several static types internal to it, and where those internal static types are in fact the main types of the program? If they wouldn't, then there is no equivalency, because this is something you see in C# code bases all the time.
Comparing the CLR and JVMs in terms of anything other than features is bizarre.
This is because the CLR/VES and JVMs are both examples of the actual important thing about general purpose computing: Computers can simulate anything, including “better” computers (that’s what Universal Turing Machine means).
But, when using one computer to simulate a “better” one, you’re always going to be giving up some raw performance that you don’t care about in order to get some features that you desperately want (after all, you are going through the enormous trouble of designing/implementing/running a simulation of another freaking general purpose computer).
Now, “better” can mean lots of things:
• The simulated computer has unbounded memory
• The simulated computer doesn’t (under|over)flow on arithmetic
• There are more people on the market that know how to program the simulated computer
• The simulated computer is more widely deployed
• The simulated computer is more secure/reliable
• The simulated computer is not controlled by a single faction
• The simulated computer readily runs programs encoded in your favorite language
• The simulated computer can easily simulate a computer that runs programs encoded in your favorite language (the subject of the article)
So, simulated computer A is strictly better than simulated computer B only in the following cases:
• A has the features that you want and B does not
• A and B both have the features that you want, but the former traded away significantly less raw performance than did the latter.
Since the CLR and JVMs are so similar with respect to performance, you should only compare them in terms of the features that you want. Personally, I think that MS was wise to go for more features at the expense of some performance since that’s really the whole point of a VM. But, unfortunately for them (and fans of CLR/VES/C#/VB/F#) they (historically) could not compete on features that quite a few people really care about (e.g. not perceived to be controlled by a single company, runs well on Linux, marketable in the Valley, etc.)
69 comments
[ 5.1 ms ] story [ 82.5 ms ] threadPerhaps there are others, but these have nothing to do with the CLR. A JVM language could easily have these features if the language supported it.
aka "if the list says C#, its because it applies to C# and not CLR"
To me, it seems like a list put together in a non-systematic way.
[1] https://medium.com/binary-dreams/from-java-to-scala-tail-rec...
Is rather have the JVM's compressed pointers and escape analysis.
https://blogs.msdn.microsoft.com/clrcodegeneration/2009/05/1...
> This is such an important concept that the JIT has a special path just for turning functions that call themselves in a tail recursive manner into loops.
However, it looks like (in at least some cases) that fsc unrolls the recursion prior to the JIT:
Isn't this statement pretty much the entirety of the reason to implement multi-tiered JITs? Do a fast JIT with few optimizations to get out of interpreted mode. Then do a slow JIT after you've collected enough statistics to make good optimization decisions.
If your work has an "inner-loop" situation, then the JVM can optimize the heck out of it using statistics specific to this particular execution. That's huge.. it opens up all kinds of concrete optimizations that aren't possible with static analysis alone (precomputing call targets, knowing what to inline, etc.). I'd wager this is a major factor in cases when the JVM occasionally outperforms C.
But no, it doesn't necessarily help.
Considering the amount of man-hours that have gone into both, the exact reasons are probably hidden under a whole bunch of implementation details.
That being said, I've run benchmarks where the JVM (version 1.8) outperforms C++ and Fortran... The JVM seemingly optimizes everything, and the amount of fine tuning you can do is amazing.
I've never heard of the CLR coming close to Java performance on large-ish apps or tasks, all else being equal.
The real reason Java apps are often associated with being big and heavy is less to do with the technology and more to do with the fact that it's so often used for bespoke enterprise apps where there's no competition and so little incentive to optimise (vs add new features).
I haven't compared the speed of a recent VS vs IntelliJ as I use a Mac now (another benefit JB got from going with Java). But of my complaints about IntelliJ none are really related to speed.
BTW if it's typing responsiveness you're thinking of, I recall that JB had some inefficient drawing algorithms which they've optimised in very recent builds (or it might even be an experimental feature still). It could cause issues on slow graphics cards. That isn't a Java issue though, it was just IJ using inefficient graphics code.
Java packages and .NET namespaces are largely similar (the CLR supports a superset of naming concepts like explicit implementation).
It's also sort of missing the big difference... the JVM is generally superior in performance to the CLR, having a better JIT and better GC performance.
The _real_ big difference is that Java has an enormous open source ecosystem, whereas much of what .NET has is ports or clones of successful Java projects.
That said, I think .NET's probably the better choice for a lot of line-of-business applications simply because the tooling allows for very rapid development of that sort of stuff, and not having easy access to Hadoop or Akka really isn't much of a handicap when you're mostly just banging out a mess of business rules.
Given that, it reads as a list of what the VM can do for you and what you'll need to do yourself in the compiler.
Though the CLR has a lot more "outs", where you can use raw pointers and such. Even put " managed" objects on an unmanaged heap. Don't think this is possible in the JVM. So you can write more C-like code in .net then you can represent in the JVM.
And I don't know if it is possible yet, but for Java 9 Graal is an extension onto of the JVM and can run unmanaged C code alongside normal code, so it should be possible.
Anyway the biggest issue I have seen in my line of work (building GPU software, and people want GPU acceleration from their language of choice) is that JVM does not provide a way to handle external resources properly. Calling garbage collection does not guarantee that the out of scope objects will be finalized. This results in either code that uses up a lot of memory or code that is very un-java like.
Yes I know try with resources exist, but the problem still exists for any temporary variables you create within the block.
If anyone here has a better solution, I am all ears.
JNI and P/Invoke are not really that different except that P/Invoke is a lot easier to implement. But CLR does not know how to cleanup unmanaged resources automagically.
edit: I removed my notes about "try with resource" and AutoCloseable
I was talking about people not wanting to move away from their language of choice which results in us having to try and support them as well.
[1] https://github.com/arrayfire/arrayfire-rust
[2] https://github.com/arrayfire
There are actually three implementations of C on top of Graal/Truffle by now.
TruffleC was the first and it maps malloc to the JVM's Unsafe malloc equivalent. So if you double free you trash the VM basically.
ManagedC ignores calls to free() and garbage collects the C programs heap.
Sulong interprets LLVM bytecode. I don't know how it does memory management.
Note that this would be a lot easier with a language like Scala or Kotlin.
``` ArrBuilder = arrs; arrs.add(A, arrs.sin(B)); Array C = arrs.get(); ```
Otherwise if you're willing to do Lombok style AST manipulation you could of course do most anything you wanted, perhaps even proper operator overloading:
``` @Arrays public void test() { Array C = A + sin(B); } ```
(Just be ready for the Java programmers to rebel on you and your "dark arts" ;) )
(For just one example, I'd really like to see a comparison of the availability+overhead of various concurrency primitives in the runtimes of the JVM vs. Erlang's BEAM, vs. Obj-C's libdispatch, etc. The languages on each platform emphasize concurrency differently, but often at the VM level things are more similar than people think.)
It doesn't matter whether the additional classes defined in the file are top level or not, they're still classes that can be used independently.
Let's just put it this way. Would a Java programmer, by way of normal operation, implement a project with a few Java files, each of which contains a type with several static types internal to it, and where those internal static types are in fact the main types of the program? If they wouldn't, then there is no equivalency, because this is something you see in C# code bases all the time.
This is because the CLR/VES and JVMs are both examples of the actual important thing about general purpose computing: Computers can simulate anything, including “better” computers (that’s what Universal Turing Machine means).
But, when using one computer to simulate a “better” one, you’re always going to be giving up some raw performance that you don’t care about in order to get some features that you desperately want (after all, you are going through the enormous trouble of designing/implementing/running a simulation of another freaking general purpose computer).
Now, “better” can mean lots of things:
• The simulated computer has unbounded memory
• The simulated computer doesn’t (under|over)flow on arithmetic
• There are more people on the market that know how to program the simulated computer
• The simulated computer is more widely deployed
• The simulated computer is more secure/reliable
• The simulated computer is not controlled by a single faction
• The simulated computer readily runs programs encoded in your favorite language
• The simulated computer can easily simulate a computer that runs programs encoded in your favorite language (the subject of the article)
So, simulated computer A is strictly better than simulated computer B only in the following cases:
• A has the features that you want and B does not
• A and B both have the features that you want, but the former traded away significantly less raw performance than did the latter.
Since the CLR and JVMs are so similar with respect to performance, you should only compare them in terms of the features that you want. Personally, I think that MS was wise to go for more features at the expense of some performance since that’s really the whole point of a VM. But, unfortunately for them (and fans of CLR/VES/C#/VB/F#) they (historically) could not compete on features that quite a few people really care about (e.g. not perceived to be controlled by a single company, runs well on Linux, marketable in the Valley, etc.)