the view from the Summit
By john.rose on Oct 01, 2008
Pizza with extra MOP
I’ll give the technical bits first, then some non-technical comments.
Here are my top-level takeaways:
invokedynamicdesign is sound, but the exposition needs more work.
- The synergy of JSR 292 with Attila Szegedi’s MOP looks very promising.
- Interface injection is going to be helpful to a lot of people, and it is not hard to implement (on top of method handles).
- Tailcall and value types will never go away. We have to plan for them.
- Unless we do this sort of innovation on the JVM, crucial multicore research will move elsewhere.
- We have to do this again next year.
The JVM change laundry listWe encouraged the speakers to talk about future directions, especially at the interface between VM and language runtime. We JVM people wanted to hear the “pain points” of the language people. There was a lot to hear!
If you have read the proposed JVM changes in my blog, you know I went primed to hear certain requests for JVM changes. Unsurprisingly, I heard them. Here is my take on what people said about JVM futures, both expected and unexpected...
- too many little classes — Iulian Dragos complained of a “class explosion” from closure creation in Scala, leading to slow startups. Paul Phillips notes that the Scala compiler creates dozens of tiny classes for every “real” source class. Charlie Nutter has spent lots of time working around the problem of adapter generation. Both Paul and Charlie asked for a better way to package such auxiliary classes, in some sort of multi-class file format. An especially annoying problem, faced when dynamic languages call Java APIs, is the choice between slow reflection and fast Java method invokers, where the latter require too many little invoker classes. Method handles should help with most of these concerns; Charlie has found they tend to make his workarounds go away. Similarly, Rob Nicholson expects to use JSR 292 features to simplify library and method calls in PHP. (I hope the JVM can also adopt a multi-class package format, some day soon.)
- new method linkage or dispatch —
The purpose of
invokedynamicis to build compact, optimizable call sites with programmer-defined semantics. There were a number of places where this might be useful. Fortress has to support dynamic multiple (multi-argument) dispatch. Chris Dutchyn, on the other hand, argued that it was a mistake in Java’s design not to perform overload resolution at runtime; he exhibited a modified JVM that rectifies original design flaw. More conservatively, Attila Szegedi showed how his metaobject protocol (MOP) can supply dynamic overload resolution logic on top of the existing JVM; combining that with
invokedynamicand Dutchyn’s algorithm might be the best way for dynamic languages to acces Java APIs.
- tagged primitives (aka fixnums) —
A number of languages are noticing that their numeric (or character) operations cannot always be optimized down to primitives, and some point out that tagged references can help remove the allocations and indirections required by boxed numbers.
Rich Hickey asked for fixnums for Clojure.
Integer.valueOfcaching impedes the optimizer. Real fixnums would be a robust fix to this problem, since they would make
Integer.valueOftrivial. Unfortunately, they also complicate paths in nearly every module of the JVM.
- guaranteed tail call optimization — Tail calls on the JVM are an old wish, back to the early days of Java (e.g., with Kawa Scheme). Doing this right is not easy and seemingly never urgent, so the JVM has not yet supported this. Requestors included Clojure, Scala, and Fortress. Along with continuations, tail calls are a key checkoff item in order for the functional community to become more interested in the JVM. (Immutable data structures apepars to be a third key item; Rich Hickey is providing good leadership on that front.)
- interface injection — There was a lot of talk about interface injection; it seemed to be a significant new degree of freedom for many of the language implementors. David Chase mentioned it in his Fortress slides, and there was a breakout session to discuss it. I got excited enough to start cutting code for it...
- continuations — Fortress has challenges with work-stealing deadlocks that arise between callers and callees in the same thread. Introspective continuations may give some extra traction on such problems. Functional programming languages usually claim want continuations, though they often make do with weakened substitutes. I think the best use case for large-scale continuations on the JVM is self-reorganizing threads; small-scale continuation-like structures might also be the right way to make coroutines (generators, fibers, etc.).
- interpreter vs. bytecodes —
Most high-level languages (e.g., JRuby, Jython, Fortress) start out running on some sort of AST interpreter. As they mature, and as they aspire to better performance, implementors start to think about byte-compiling them. At this point, the flexibility and compactness of
invokedynamicshould be a big help. By the way, Scala cuts across this trend, by decompiling library bytecodes up into ICode (their IR), to assist with non-local optimization.
- byte compiler vs. JIT — JRuby does delayed byte-compilation, playing the same games as HotSpot, except on the virtual metal. They have noticed that this sometimes confuses the JVM JIT, which assumes that an application’s bytecode working set is stable. Jython would also benefit from a way to deoptimize from fast bytecodes back to a more flexible (but slower) prior representation, in which stack frames are fully reified. Doing this gracefully is an open problem.
- naked native methods — JRuby needs Posix calls; the Java APIs do not supply many of them (a “glaring” flaw). The right answer is probably not to wrap all the missing Posix calls in Java, but rather to build a lower-level native call facility into the JVM.
- strange arrays — Clojure has some wonderful functional-style collection data types which could be tuned even more if the JVM offered invariant arrays, arrays with program-defined header fields, or arrays of tuples. Fortress (see a pattern?) made a request for arrays of value types.
- a fast numeric tower —
The Java world needs a flexible, performance-tuned numeric tower implementation.
(Clojure mentioned this as a pain point; presumably the other Lisps could us it also.)
Kawa, which is admirably factored software, has such a thing. Probably the right way to present it now is via a MOP and
invokedynamic, which would allow maximum flexbility and optimizability. Brian Goetz points out that this might also need value types to get right, so a number can live in two registers (the fast version and the slow version, like Rhino does). Note that arithmetic is a poster child for optimized dynamic multiple dispatch. Cliff’s talk noted that the JIT code for Clojure (while generally good) suffered from overflow checks; this sort of thing has to play well with the JIT, which means the JVM manufacturers should way attention to optimizing the library—when we build it.
- miscellaneous, persistent ideas —
Fortress could also use floating point operations with the full panoply of rounding modes, and a way to profile subprograms (e.g., to pick the fastest). Clojure asked for a
JVM earthquakesSome ideas promised to stretch the fundamentals of the JVM in unpredictable ways. Stress on Java’s heap model include immutability, transactions, and reified types. Fortress wants transactions; it is hard to say how to mix this uniformly into the heap. They are also worried about the problem of “double tagging” objects with both a JVM-level erased type and their own types. (BTW, there were some favorable comments, from those who had worked with both JVM and CLR, about the simplicity of working with the JVM’s erased types, as opposed to the unerased type parameters of CLR, which tend to get in the way of dynamic language code generation.) Finally, Erik Meijer challenged us to design systems which abstract over (and thus isolate) all side effects, including seemingly innocent ones like cloning an object.
Threads are also stressed by the new languages. In Fortress, they do not always mix smoothly with workstealing parallelism or with coroutines required by complex comprehensions. Ideally, there should be a way to break a computation into “fibers” which can be created and completed in a few instructions. Parrot (a continuation-based VM) may be able to offer insight into this. Clojure offers a concept of agent which clearly maps to JVM threads, but may require something finer if it is to scale well.
Neal Gafter explained how, as the JVM supports new languages, the Java APIs risk behind left behind on the wrong side of a semantic gap, between Java (circa 1997) and the consensus features of the new languages, all of which include some sort of lambda expressions (closures). They also often include richer dynamic dispatch, proper tail calls, exotic identifiers, and continuations. This could be a stressful change, if it requires significant retrofitting of Java APIs. We can do this: A similar stressful change was the retrofitting of the standard Java libraries to generics, and I think that worked out smoothly enough.
We (in the Da Vinci Machine project incubator) are working on some of the technically more difficult JVM changes, including tail calls, continuations, and extended arrays. As noted above, fixnums also look technically difficult, because they touch just about every corner of the JVM, so (as far as I know) nobody is looking at them. Some changes that look difficult today may turn out to be practical, once somebody has applied enough thought and experimentation.
JSR 292Naturally, I heard (and was responsible for) lots of talk about the JSR 292 effort,
invokedynamic, and other Da Vinci Machine subprojects. One breakout session was titled “Invokedyamnic — the details”. Here's a picture of our confabulation:
The tale of
From left to right are yours truly, Jochen Theodorou (Groovy), Rob Nicholson (Project Zero PHP), Tom Enebo and Charlie Nutter (both of JRuby), Fredrik Öhrström (JRockit), Rémi Forax (JSR 292 backport), and Attila Szegedi. And here are Rémi and Jochen writing up method call scenarios:
Parallel call site examples
One of the more useful hints I got from the meetup was about the pitfalls of expressing
invokedynamic as just another sort of interface invocation. (We do this to avoid breaking static analysis tools, and because
invokeinterface has a four-byte operand field, not because
invokeinterface have similar semantics.) The exposition of the JSR 292 Early Draft needs to be adjusted to make it clear that
invokedynamic has no predefined receiver, that all stacked arguments are treated equally. Thanks, Fredrik.
It seems clear that, as the
invokeinterface encoding is a workaround for static tools, that when in the future we do a separately motivated verifier-breaking change (say, for tailcalls or tuples), it will be wise to revisit the encoding of
invokedynamic. I suppose we will want to adopt the unused invoke bytecode for a clean, receiverless
invokedynamic, and deprecate the old compatible encoding. But not until we have enough changes planned to warrant a verifier-breaking change. And even now, it seems right to express dynamic calls in pseudo-Java using static syntax:
Dynamic.greet("hello", "world", 123); // => ldc "hello"; ldc "world"; ldc 123; // invokeinterface Dynamic.greet(String,int)Object
So, how did it all go?Overall, people seemed very happy with the Summit. I know I was.
- Our goal was to fill Sun’s largest classroom (90 seats) with engineers and researchers.
Result: There were about 75 registered attendees, rounded out by a number of local Sun engineers attending part time.
- Since it needed to be a small meeting, we wanted a true summit, of lead designers and implementors working on JVM languages and related technologies.
Result: Wonderfully, our visitors were a who’s–who in that world, including two out of three original Java Language Spec. authors, JVM team members from Sun (HotSpot, Maxine, Monty), IBM (J9), Oracle (JRockit), and Azul, senior CLR designers, and key engineers from an astonishing variety of JVM languages. We even saw a Parrot.
- We wanted lots of conversation, for the designers learn from each other and tune up their plans for the future.
Result: Except during the talks, the classroom (plus all available breakout spaces) was full of the buzz of intense technical conversations. Many people stayed late trying to finish the last chat, even after the food was gone and it was time to go home. On the comment cards, the highest score (4.9 out of 5) went to the category “quality of conversations you had”.
- We wanted to be good hosts, and didn’t want logistics to get in the way of the conversation.
Result: For a conference organized solely by engineers, it was pretty good, and we think we can make it better next time. Actually, we had the good fortune of enlisting a project manager, who helped us keep our actions coherent. (Thanks, Penni!) The comment cards were both encouraging (4.4 out of 5, our lowest category) and helpful in detail.
- We wanted to capture much of the discourse, so that more people could share in the talks than would fit in the Sun classroom.
Result: Most of the slides, as well as interesting extra information, is captured as informal proceedings in the Summit wiki. Have a look! (I did learn that wiki is a reasonable way to assemble a conference proceeding, but a bad medium for interaction. Note that all the Talk pages are empty, and the OpenSpaces organization moved onto a physical bulletin board.)
P.S. Thanks, Oleg Pliss, for the great pictures!
(The not-great ones are frames from my video camera.)
Laptop – 1, Rose & Goetz – 0