4 ms·
Performance is why you should use different implementation details on the metal. The reason why there's a distinction at the language level, though, is because
by mumblemumble 6y ago
Performance is why you should use different implementation details on the metal. The reason why there's a distinction at the language level, though, is because Java's designers chose to leak implementation details into the language's high-level semantics.
This distinction wasn't such a big deal in 1995. It was perhaps even the preferred way of doing things according to contemporary values. Nowadays, though, Java is a very different language that lives in a very different cultural milieu. So, nowadays, the language-level distinction, in combination with some other design decisions that came later, is absolutely a source of performance problems. Any code that tries to do things like store numbers in one of the standard collection classes will quickly devolve into a horrible mess of memory overhead and pointer chasing. It's led to this perverse situation where it's honestly not too hard to beat Java performance in a dynamically typed language like Clojure or Python, simply because dynamic languages make it easy to encapsulate the implementation details (and therefore to choose a more performant run-time implementation) in a way that Java's type system doesn't really allow.
You can still keep Java toward the top of the well-known performance benchmark leaderboards, but only by coding as if Java 5 never happened. Which is an option that's only tenable for toy use cases like benchmarks.
- mrkeen 6y agoI don't disagree about the penalties of boxing and chasing pointers and moving things around on the heap. And I reiterate my dislike of the ergonomics of having a primitive/Object distinction, especially when it comes to collections. But I'm skeptical of the argument that the language designers could have improved performance while hiding the implementation details from us. If anything it feels like the reverse is true. Here's a few additional implementation details I'd like access to: * I want to allocate an object (by value - not by pointer) straight onto the stack (not the heap). * I want to specify that a class completely opts out of inheritance so that the compiler knows which exact method I'm calling (static dispatch) and can inline appropriately. * (If tail-call optimisation existed) I'd like the ability to annotate a method as such, and have the compiler reject it if TCO couldn't be done. > It's led to this perverse situation where it's honestly not too hard to beat Java performance in a dynamically typed language like Clojure or Python, simply because dynamic languages make it easier to encapsulate the implementation details (and therefore to choose a more performant run-time implementation) than Java's type system does. I'm skeptical of this too. It sounds a lot like the "Java faster than C++" I've been hearing for like 15 years, e.g. > https://trs.jpl.nasa.gov/bitstream/handle/2014/18351/99-1827.pdf https://trs.jpl.nasa.gov/bitstream/handle/2014/18351/99-1827... > Java performance can exceed that of C++ because dynamic compilation gives the Java compiler access to runtime information not available to a C++ compiler.
- mumblemumble 6y agore: performance, I am guilty of making a general statement when I should have made a much more specific one: It's not too hard to beat Java for numerical applications. The root of the situation is that it's pretty easy for the dynamic languages to choose an unboxed representation on the back-end while still exposing a reasonably generic interface to the programmer. The ergonomic payoff there is immense. I submit, for example, that the just obscenely rich ecosystem of high performance data analysis packages that have been built on top of numpy just could not happen in Java. Java forces you to either accept boxing, or write specialized overloads for every specific combination of argument types you want your function to be able to handle. Java does have some specialized numeric libraries, generally built using code generation, but they aren't widely used because the ergonomics aren't great, and building any additional general-purpose tooling on top of them requires still more code generation. And that's just not a fun way to work. It's brittle.
- singron 6y ago> * I want to allocate an object (by value - not by pointer) straight onto the stack (not the heap). The JVM does escape analysis to perform this in cases where it can prove the lifetimes will work. > * I want to specify that a class completely opts out of inheritance so that the compiler knows which exact method I'm calling (static dispatch) and can inline appropriately. I think this is the final modifier? The problem with all of these are that despite java having static types, the JVM is fairly dynamic. It's difficult to really guarantee anything at the source level because you don't know what classes will actually get loaded into the JVM. Even without inheritance, you can even load multiple versions of the same class simultaneously with different classloaders. Thus it's difficult to guarantee any optimizations at the source level in java. The JVM has to see what classes actually get loaded before it can make optimization decisions. Having different classes at runtime and compile time is actually pretty common too. E.g. in gradle if you have a implementation dependency on liba, which depends on libb=2.0, you can also have a runtime dependency on libc, which depends on libb=3.0. This would cause you to compile against libb=2.0 but run against libb=3.0.
- srtjstjsj 6y agoHuh? Java has Interfaces. fastutil.di.unimi.it provides unboxed Generic collections and is over 10 years old.