News for hackers. Stuff that matters.
Re:Chris Lattner on Swift and dynamic dispatch by golergka on Sunday December 13, 2015 @04:39AM (#10725903)
Can someone elaborate on what methods JIT uses to boost perfomance as compared with AOT compilation? Do modern JIT compilers use statistics about what code actually got called to optimize call dispatch? Where can I find more info about this topic?
Re:Chris Lattner on Swift and dynamic dispatch by x0x0 on Sunday December 13, 2015 @05:26AM (#10725988)
You should really watch "A jit does that" video by cliff click, which is just excellent, but the brief answer is this:

Java has global knowledge of all loaded classes which implement an interface. So the easy answer is, for a given call site, only one class implements a given interface. Wonderful, devirtualize it (direct calls, inline if the methods are small.) As compared to c++, you skip the vtable memory load and the jump.

They're also smart enough that, even if multiple classes implement an interface, they count who gets called at a call site and speculatively devirtualize (ie devirtualize or inline with a guard check). This is a big piece of why they're so much faster than c++.

Imagine this situation: I have an interface for probability distributions, and that interface is implemented by eg gaussian, exponential, weibull, etc. At run time, I draw a million times from one of them, then I pick another by asking the user, draw a million times, etc. So there's a tight loop that bangs on that interface over and over. In c++, you cannot devirtualize that; each call to random->getRand() involves a vtable lookup and a jump. Java will notice that, eg, you picked gaussian so the call site will be devirtualized, skipping the vtable lookup and possibly the jump. Then, later, when the user says draw a million times from poisson, it will re-virtualize it, notice it's monomorphic with a different implementation, and revirtualize it. Inlining code based on usage, and being able to change that inlining at runtime is often a very effective performance improvement technique.

Now the ugly bit, and why the jvm is one hell of a piece of engineering, is you can load classes at any time during the life of a java program, so even if at start there is only one known class implementing a given interface that can change. Therefore, you have to be able to go to already generated code and revirtualize/deoptimize it. But this magic all just works.

Re:Chris Lattner on Swift and dynamic dispatch by fotcorn on Sunday December 13, 2015 @07:27AM (#10726173)
https://www.youtube.com/watch?v=uL2D3qzHtqY

A JVM Does That? Google Tech Talk by Cliff Click

Re:Chris Lattner on Swift and dynamic dispatch by xxs on Sunday December 13, 2015 @05:02AM (#10725950)
JITs do know the call target. This is a quote from "A JVM does that?"[0]:

  C++ avoids virtual calls – because they are slow
  ● Java embraces them – and makes them fast
  ● Well, mostly fast – JIT's do Class Hierarchy Analysis
  ● CHA turns most virtual calls into static calls
  ● JVM detects new classes loaded, adjusts CHA
  – May need to re-JIT
  ● When CHA fails to make the call static, inline caches
  ● When IC's fail, virtual calls are back to being slow
----

There are several misconception about Java in the write-up. The real reason there won't be a bootloader is the need for the Java runtime and the need to GC. Unoptimized virtual calls won't affect any bootloader for that matter (and you can/should use static and/or private methods for the inner loops)

[0]:https://www.youtube.com/watch?v=uL2D3qzHtqY

Re:Chris Lattner on Swift and dynamic dispatch by pvg on Sunday December 13, 2015 @05:17AM (#10725972)
He doesn't say you won't see a Java bootloader because of unoptimized virtual calls, he says it's because the runtime is too big. CL: It also means that Java doesn’t “scale down” well to small embedded systems that can’t support a JIT, like a bootloader.
Re:Chris Lattner on Swift and dynamic dispatch by xxs on Sunday December 13, 2015 @05:22AM (#10725981)
OTOH, since the compilation model assumes a JIT, this means that purely “AOT” static compilers (which have no profile information, no knowledge of class loaders, etc) necessarily produce inferior code. It also means that Java doesn’t “scale down” well to small embedded systems that can’t support a JIT, like a bootloader.

This the quote. He talks about the JIT only, no runtime mentioned.

Re:Chris Lattner on Swift and dynamic dispatch by pvg on Sunday December 13, 2015 @05:31AM (#10725997)
He might somewhat tersely be conflating JIT and 'runtime'as whole - see the mention of classloaders but his meaning is clear, it's about size rather than performance. 'It also means' etc.
Re:Chris Lattner on Swift and dynamic dispatch by masklinn on Sunday December 13, 2015 @06:07AM (#10726050)
> it's about size rather than performance.

The tradeoff really: what he's saying is that you can JIT java to get speed but you'll lose size (resident), or you can AOT compile java to improve size but then you lose speed because calls may not be possible to devirtualize (unless you have PGO maybe? You'd lose some speed and size to guards and duplicated dispatch though)

Re:Chris Lattner on Swift and dynamic dispatch by pvg on Sunday December 13, 2015 @06:19AM (#10726062)
You can't really AOT compile java and have it be java and it seems fairly obvious to me he's talking about size and runtime-related things, in which he throws in a JIT (i.e. the compilation model 'assumes' a JIT). I think he's just being slightly loose with his terminology because he's just replying on a mailing list without expectation that random nerds on an internet forum will be talumidcally dissecting his semiotic intent.
Re:Chris Lattner on Swift and dynamic dispatch by masklinn on Sunday December 13, 2015 @06:40AM (#10726095)
> You can't really AOT compile java and have it be java

Of course you can.

> and it seems fairly obvious to me he's talking about size and runtime-related things

I'm not sure why you're so intent in reinterpreting his rather plain words (and insulting people when they don't agree with your broad reinterpretation).

Lattner is literally just noting that an AOT-compiled java program has no knowledge of java's dynamic semantics (classloader &al), so it can't devirtualize calls and must necessarily pay the dynamic dispatch cost, in the same way e.g. messages in objective-c can't be devirtualized. In essence, an AOT java is slightly better than a straight interpreter but not by much.

The whole essay is about static versus dynamic dispatch and how languages provide for those, it's not exactly a stretch to assume that when Lattner talks about inferior code that's what he's talking about: bootloader = no JIT = pervasive dynamic dispatch = slow, you don't have to bring your own pet issues into that.

Re:Chris Lattner on Swift and dynamic dispatch by comex on Sunday December 13, 2015 @11:03AM (#10726645)
Not nearly as bad as an interpreter, especially if you do whole program optimization. In fact, Android switched from the Dalvik JIT to ART, which compiles AOT-ish.
Re:Chris Lattner on Swift and dynamic dispatch by pjmlp on Sunday December 13, 2015 @01:27PM (#10727231)
> You can't really AOT compile java and have it be java

IBM J9, Aonix PERC, Excelsior JET, JamaicaVM, RoboVM are just a few of the commercial JVMs that also AOT compile Java.

Oracle is also planning to have AOT as an option in future versions of the compiler.

Re:Chris Lattner on Swift and dynamic dispatch by Someone on Sunday December 13, 2015 @01:35PM (#10727270)
I don't think you can have a JIT without a runtime. Something has to decide when it is 'just in time' to compile a piece of code, and something has to run the piece before it gets JITted. Or am I overlooking a narrower definition of 'runtime'?
Re:Chris Lattner on Swift and dynamic dispatch by icebraining on Sunday December 13, 2015 @06:05AM (#10726047)
Maybe I'm missing something, because people have implemented JVMs on embedded systems, including GC. An example is lejOS, which runs on the Lego Mindstorms, including the original RCX with its 8-bit microcontroller and 32K of RAM.
Re:Chris Lattner on Swift and dynamic dispatch by wolf550e on Sunday December 13, 2015 @04:52AM (#10725923)
Read about polymorphic inline caches which Java and Javascript JITs do.
Re:Chris Lattner on Swift and dynamic dispatch by ubernostrum on Sunday December 13, 2015 @05:26AM (#10725989)
Here's a good, if old, quick intro to some of the stuff the JVM does:

http://blog.headius.com/2008/05/power-of-jvm.html

Choice section:

The JVM is basically a dynamic language runtime. Because all calls in Java are virtual (meaning subclass methods of the same name and parameters always override parent class methods), and because new code can be loaded into the system at any time, the JVM must deal with nearly-dynamic call paths all the time. In order to make this perform, the JVM always runs code through an interpreter for a short time, very much like JRuby does. While interpreting, it gathers information about the calls being made, 'try' blocks that immediately wrap throws, null checks that never fail, and so on. And when it finally decides to JIT that bytecode into native machine code, it makes a bunch of guesses based on that profiled information; methods can be inlined, throws can be turned into jumps, null checks can be eliminated (with appropriate guards elsewhere)...on and on the list of optimizations goes (and I've heard from JVM engineers that they've only started to scratch the surface).

This is where the call site optimizations get their second boost. Because JRuby's and Groovy's call sites now move the target of the invocation much closer to the site where it's being invoked, the JVM can actually inline a dynamic call right into the calling method. Or in Groovy's case, it can inline much of the reflected call path, maybe right up to the actual target. So because Groovy has now added the same call site optimization we use in JRuby, it gets a double boost from both eliminating the dispatch overhead and making it easier for the JVM to optimize.

Of course there's a catch. Even if you call a given method on type A a thousand times, somewhere down the road you may get passed an instance of type B that extends and overrides methods from A. What happens if you've already inlined A's method when B comes along? Here again the JVM shines. Because the JVM is essentially a dynamic language runtime under the covers, it remains ever-vigilant, watching for exactly these sorts of events to happen. And here's the really cool part: when situations change, the JVM can deoptimize.

This is a crucial detail. Many other runtimes can only do their optimization once. C compilers must do it all ahead of time, during the build. Some allow you to profile your application and feed that into subsequent builds, but once you've released a piece of code it's essentially as optimized as it will ever get. Other VM-like systems like the CLR do have a JIT phase, but it happens early in execution (maybe before the system even starts executing) and doesn't ever happen again. The JVM's ability to deoptimize and return to interpretation gives it room to be optimistic...room to make ambitious guesses and gracefully fall back to a safe state, to try again later.