6 ms·
I have seen much the same made about Java versus C++ for the past 25 years that the Java byte code JIT would have more information and thus be able to optimize
by RcouF1uZ4gsC 6y ago
I have seen much the same made about Java versus C++ for the past 25 years that the Java byte code JIT would have more information and thus be able to optimize and do stuff like devirtualization better.
However, that has not planned out.
There are a few reasons for this. First C++ and Rust optimizers can do amazing things when they are given time. In addition, I think devirtualization is not as big a deal in C++ and Rust because in general you avoid writing code that uses virtual functions when you are writing performance sensitive code and instead use things like templates/generics where there is no indirect function calls.
- kevingadd 6y agoYour argument basically boils down to "If you write fast C++ it will be fast", which is true. But a significant fraction of code out there is not fast C++ written by experts to be fast. This is different than "Java will be faster than C++ because of HotSpot" arguments, because java is competing with C++. This is not a competition between JS and native C++, it's a competition between JS and WASM.
- astrange 6y agoThe main problems with Java aren't being JITted, it's that it's not expressive enough. It doesn't have SIMD (yet) or value types (yet…?). I would expect a JIT to not really be able to find a lot of magic optimization opportunities, though maybe there are some, and it'd actually be annoying if it could. The most important thing in a tool like that is predictability, because you can't make development decisions based on magic.
- kevingadd 6y agoOptimizations like 'these two function arguments are always int31' in v8 or spidermonkey are 100% predictable at this point and result in all your type checks and boxing being eliminated, and with the known types it also becomes much cheaper/faster to create object instances (since now if you store those values into properties of an object, that object's shape is fully known). Various properties like this can extend out into larger parts of your JS application. There's still a lot of magic you can't rely on, but you'd be surprised how much you CAN rely on. Asm.js was built on this observation: If you write your JS following some basic rules it's actually pretty easy to land on predictable, well-optimized paths. Of course, one of WASM's advantages is that by design you're almost always on those paths and don't have to worry.
- pjmlp 6y agoIt surely has had support for AVX for several releases, although via the autovectorization support, and explicit SIMD has been made available as preview on Java 16.
- astrange 6y agoAutovectorization is the kind of magic you can't rely on. It sort of works on a single platform but you will always run into cases it doesn't handle even if you own your own team of autovectorization engineers who tell you it's perfect.
- pjmlp 6y agoA magic shared with C, Fortran and C++ compilers, among others, so support is there.
- sudosysgen 6y agoCompiled autovectorization is miles more reliable than JIT autovectorization.
- kaba0 6y agoAt the other hand, the explicit Vector API will use the correct "flavor" of SIMD instructions on the platform and will gracefully fall back to non-simd version if it is not supported. And as far as I know, the SIMD story is quite bad with C.
- sudosysgen 6y agoYes it's quite bad with C. With C++ and Rust it's much much better when you do it properly.
- pjmlp 6y agoWhen you do it properly is the big question.
- MaxBarraclough 6y ago> it's that it's not expressive enough That may be part of it, but I imagine the JVM's safety obligations are also a significant factor. If the JIT can't elide array bounds checks, checks must be performed at runtime. Runtime type checks might be needed. Runtime arithmetic checks might also be needed. The JVM is also more constraining regarding concurrency gone awry, than the C/C++ memory model. [0] More broadly, the JVM's lack of undefined behaviour constrains the optimiser in ways the C/C++ approach does not (although I'm open to the idea that it's overstated how much of a performance win is owed to C and C++ having many kinds of undefined behaviour). And of course there's the GC and Java's high object-churn, even where lifetimes are known statically. To my knowledge, escape analysis (the relevant family of JIT optimisations) still hasn't really addressed this. [0] https://softwareengineering.stackexchange.com/q/262428/ https://softwareengineering.stackexchange.com/q/262428/
- kaba0 6y agoThe JIT can elide array bound checks really often, and most "low hanging" optimizations are solved quite cleverly (it's way out of scope for my knowledge, but I remember reading that null checks are elided by trapping segfaults? Does it make sense?). There is no over/underflow checks so I don't know what you mean by arithmetic checks -- in pure number crunching the JVM is insanely fast. And you are right in that many Java libs/programs are quite happy to create garbage, though with generational GCs it is really cheap. Escape analysis is great, but primitive classes in Project Valhalla will solve this last problem of object locality.
- MaxBarraclough 6y ago> null checks are elided by trapping segfaults Sounds right. No need to generate instructions to perform the check if you can rely on a hardware trap, by means of signal-handling cleverness. > There is no over/underflow checks so I don't know what you mean by arithmetic checks -- in pure number crunching the JVM is insanely fast. Integer multiplication, addition, and subtraction, are all defined in Java to have wrapping behaviour, and are easily implemented. Whatever the input values, there's no way those operations can fail. (Incidentally, this is a terrible way of handling overflow. This turned up recently in discussion. [0]) Division is trickier. In Java, integer division by zero results in an exception being thrown. Apparently JVMs can implement this with signal-handling cleverness similar to dereferencing null references. [1] Two's complement integer division has another edge case, which is undefined behaviour in C/C++ but which, iirc, results in an exception in Java: INT_MIN / -1. I believe the JIT has to emit instructions to check for this, as it's not possible to leverage signal-handling there. I don't know how well modern Java performs in floating-point arithmetic. Here's an old tirade about it [2] and discussion. [3] > with generational GCs it is really cheap. At the risk of going off topic: doesn't Java tend to perform somewhere around 60% the speed of C/C++, while using considerably more memory? Perhaps the GC isn't to blame, but clearly the blame belongs somewhere. It's like the way advocates of Electron will insist that modern HTML rendering engines are fast and efficient, the DOM is fast and efficient, and JavaScript is fast and efficient... and yet here we are, with Electron-based applications reliably taking several times the computational resources of competing solutions using conventional toolkits. > primitive classes in Project Valhalla will solve this last problem of object locality Interesting, sounds like the kind of ambitious initiative that will require deep changes to the JVM. [0] https://news.ycombinator.com/item?id=26666013 https://news.ycombinator.com/item?id=26666013 [1] https://www.javaer101.com/en/article/3117893.html https://www.javaer101.com/en/article/3117893.html [2] (PDF) https://people.eecs.berkeley.edu/~wkahan/JAVAhurt.pdf https://people.eecs.berkeley.edu/~wkahan/JAVAhurt.pdf [3] https://news.ycombinator.com/item?id=6585828 https://news.ycombinator.com/item?id=6585828
- kaba0 6y ago> The most important thing in a tool like that is predictability, because you can't make development decisions based on magic. Fortunately you've got the best profiling tools available, so you don't have to guess. And also you get to see the relative importance of the function you try to optimize, whether that actually is the bottleneck (and actually people often guess wrongly where the bottleneck is)
- v8dev123 6y agoYou don't have to be an expert to write Fast C++ that beats Java. I wrote a simple for loop and when compiled with -O3 it beat the Java version of it. You just need to know your tools, that's it. Plenty people forget -O flag and existence of libraries like Folly. If you combine PGO along with these must knows, you seriously will become much more faster.
- kaba0 6y agoA for loop is not too interesting application -- it is not what Java optimizes for, and chances are you didn't benchmark it correctly. To optimize your program in a low level language you have to basically have a whole plan for the architecture of your program beforehand, and every major change to that will break your optimizations. Also, don't forget about non-standard object life cycles, which is really common. Complex C++ programs basically employ their own GCs, which will be inferior to any one included in the JVM. Of course low-level programs have their place (plenty of), eg. audio processing, embedded, million other, but the average business/CRUD app will be faster* both to execute and to produce in Java, as well as better maintainable. * With enough time a competent team could of course write a faster version of it in C++, but it's not a good use of their time, and you would be surprised how hard it, especially with ever-changing requirements.
- v8dev123 6y agoC++ is not just a low level language. It's consists both high level and low level. C however a low level language. I bench-marked using Intel vTune. for loop is interesting. It's why Tensorflow Core written in C++ instead Java. I don't know any complex C++ program that employ their own GCs when C++ has RAII which is superior to GC. Just give a try for C++11/14/17 and you will see which one is more maintainable and expressive. Look at Chromium codebase. It's the most beautiful codebase I've ever been to. I've done a lot of CRUD web apps in C++ using expresscpp [1] and I would say it's easy as ABC. A lot of Java folks haven't tried C++11/14/17 (Modern C++). C++ is Zen of OOP. [1] https://github.com/expresscpp/expresscpp https://github.com/expresscpp/expresscpp
- 6y ago
- pjmlp 6y agoOther than 3D AAA game engines, all the C++ software that I replaced with either Java or .NET solutions has kept the customers happy and lowered the TCO of their products. This wasn't tiny CLI that occasionally lands on HN, rather large scale desktop applications or distributed computing clusters. Winning micro-benchmarks is not everything, which is why except for Windows with WinUI (which still remains to be seen if it can move windevs away from Forms/WPF in its current incomplete state), all OS vendors are migrating to other languages for their App development SDKs, leaving C++ and Rust only for low level OS components.
- v8dev123 6y agoAre those C++ ones written in pre-C++11 ? I don't think C++ and Rust are for just low-level. I've build a lot of GUI apps and distributed ones with C++ QT is a beast. The issue with Java is the reverse engineering. Back in 90's, The main selling point for Java was to prevent it, because at that time the bytecode was hard to understand at least. Now tools has grown and it's fairly easy to reverse engineer Java, even if one obfuscate it. As for C++, Inline code and template code make it pain in ass. I'm sure big companies that care about intellectual property would use C++ over Java anytime. C++ also has mature obfuscation tools that make it even more difficult. Java has it's place. It's great language if you use in server side or isolated env (From commercial viewpoint). Nevertheless, I've built many web apps using C++ too.
- pjmlp 6y agoThey were and are written in all sorts of C++ flavours, including past C++11. Reverse engineering is never an issue with Java if one actually uses the right tooling, commercial AOT compilers exisst since around 2000, it is a matter of buying them. I assume JetBrains and Google are big companies.
- v8dev123 6y agoGoogle where I works at is largely a C++ shop despite Android Java. AOT compilers exists for Java, Net, Javascript. I however doubt the user experience of those. For example, GraalVM mentions the following, "There is a small portion of Java features are not susceptible to ahead-of-time compilation, and will therefore miss out on the performance advantages. To be able to build a highly optimized native executable, GraalVM runs an aggressive static analysis that requires a closed-world assumption, which means that all classes and all bytecodes that are reachable at run time must be known at build time. Therefore, it is not possible to load new data that have not been available during ahead-of-time compilation."