Just out of curiosity, what will this actually enable? Optimizations?
What can we expect might perform faster -- calling closures?
Functional function-calls such as map, reduce, etc.? Multimethods,
protocols, and things like that?
--
Protege: What is this seething mass of parentheses?!
Master: Your father's Lisp REPL. This is the language of a true
hacker. Not as clumsy or random as C++; a language for a more
civilized age.
--
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clo...@googlegroups.com
Note that posts from new members are moderated - please be patient with your first post.
To unsubscribe from this group, send email to
clojure+u...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
Cheers,
Aaron Bedra
--
Clojure/core
http://clojure.com
It's important to note that this only applies to method calls, not
function invocations.
> Currently all dynamic languages targeting the JVM generate code
> that cannot be easily optimized by the JVM before invokedynamic
> was available.
That's not really true; it's easy in Clojure to optimize with type
hints. Since Ruby isn't designed to run on the JVM, it doesn't have
any syntax for type hints to avoid reflective calls. I believe method
calls that use invokedynamic are much faster than reflective
(non-hinted) method calls, but still a bit slower than type-hinted
calls. So if you are in a tight loop you'll need to type hint anyway.
I suspect we'd get a lot more bang-for-the-buck out of type inference
than invokedynamic since that may allow us to get the benefits of type
hints without the grunt work of scattering declarations everywhere.
-Phil
For the record, I didn't mean to discourage anyone from working on it,
only to explain why it hasn't been implemented yet.
-Phil
Since Ruby isn't designed to run on the JVM, it doesn't have
any syntax for type hints to avoid reflective calls. I believe method
calls that use invokedynamic are much faster than reflective
(non-hinted) method calls, but still a bit slower than type-hinted
calls. So if you are in a tight loop you'll need to type hint anyway.
A significant speed improvement without resorting to type hints would be a pretty huge win from a programmer's standpoint.
Hmm... If you didn't have to worry about Java <7 compatibility, for one thing with invokedynamic you could remove a lot of code from Clojure. No more IFn or AFn. You simply have a method handle.
Second, I think it would allow the JVM to have a better view into optimization, and would allow the JVM to optimize something speculatively and allow for Clojure tell the JVM when to deoptimize with a SwitchPoints and MutableCallSites.
I don't think Phil's comparison is actually a fair one. If you're going to use type hints or type inference, then you're basically moving more towards static typing, which of course will be faster.
this is a brilliant report - thanks very much. Even though you haven't done anything but back up the assertions from Clojure/core, I find your report much more reassuring (from a personal perspective) than just blindly believing something.
I think the Clojure community would benefit from further such reports/discussions. Although they might not always directly improve Clojure the language, they clearly help inform and educate Clojure the community - which can only be a good thing.
We shouldn't just be satisfied with knowing which direction we're headed - we should be trying our best to collectively help each other understand why we're headed in that direction. This report has definitely given me more understanding of why.
Thanks once again,
Sam
So, after setting up a JVM 7 environment to play with Clojure, and enthusiastically rummaging through the codebase, I have good news and bad news. :)
So, when Clojure calls a function, it either already has the instance in its entirety (a lambda) or it finds it by dereferencing a binding. Since all functions are instances that implement IFn, the implementation can then call invokeinterface, which is very efficient.
[See clojure.lang.Compiler#InvokeExpr.emitArgsAndCall]
Clojure can get away with this especially easily because the only variant for function signatures in Clojure is arity. So, all we need is a simple switch to call the IFn method with the correct arity. (Interestingly, this means that Clojure has a fixed set of such signatures, and thus a fixed upper limit to arity: 20, in case you were wondering.)
In a language like Ruby, methods are not so free floating, and have much more complex invocation styles as well as flexible signatures. (Clojure has only one style: function calls are simple forms.) So, in order to use invokeinterface, JRuby implementors would have had to create special classes *per* method *per* invocation style if they wanted to be efficient. But this is impossible, because so many classes would exhaust the perm-gen. For those languages, invokedynamic is a terrific solution, because it lets them dynamically link the implementation to the caller via a flexible "bootstrapping" mechanism, allowing them to do entirely without the extra class definition. Since all Clojure functions share essentially the same class as well as interface, none of these challenges exist.
Another aspect is the immutability of these IFn instances: you can't refactor them at runtime, all you can do is change the bindings to refer to new functions. So, Clojure achieves runtime dynamics by letting you simply rebind new functions to existing Vars, Refs, Atoms, etc., and the same invocation route continues as usual. In a language like Ruby, the bootstrapping mechanism of invokedynamic lets implementors change the base linkage to reflect a new signature for the method. Again, a terrific JVM 7 feature that Clojure simply does not need.
Another issue I examined was how Clojure calls non-Clojure JVM code, thinking that perhaps there invokedynamic would be useful. But, Clojure again has a straightforward approach that poses no problems. Here, Clojure very directly calls non-Clojure code using invokestatic, invokevirtual or invokeinterface as appropriate. A Clojure form with the "." or ".." notations translates quite directly to a JVM method call. In fact, there's no significant difference between how Clojure compiles such code and how a Java compiler (such as javac) would compile such code. Clojure, too, explicitly handles boxing and unboxing of primitive types and other conveniences. Where Clojure slightly differs is in how it picks the correct method to call (it does method reflection, but only once during the compilation phase), and its coercion of types to match the called method -- after all, its type system is dynamic.
[See clojure.lang.Compiler#InstanceMethodExpr, StaticMethodExpr, etc.]
[snip]
Another note re: JRuby, is that it also has a very nice implementation of constants that makes use of invokedynamic, which ends up being somewhat similar to the problem of switching functions dynamically, but also uses a "SwitchPoint" mechanism to allow for very efficient concurrent mutability of constants. Indeed, JRuby makes use of every part of JSR-292! Clojure doesn't need consts, of course, because everything is immutable.
In summary, Clojure's dynamics are already handled as well as could be on the JVM, at least as far as I can see.
Hmm... If you didn't have to worry about Java <7 compatibility, for one thing with invokedynamic you could remove a lot of code from Clojure. No more IFn or AFn. You simply have a method handle.
Actually, Clojure's solution is almost identical to a method handle. An instance of AFn is not much different from an instance of MethodHandle, from the JVM's standpoint. The problem MethodHandle solves for languages like JRuby is that they need *different classes* for each method handling, whereas Clojure gets away with instances. Where using MethodHandle becomes important is when you want to participate in the invokedynamic bootstrapping, which expects MethodHandle instances. If Clojure ever needs to do this, it would be a simple one-to-one translation from AFn.
Second, I think it would allow the JVM to have a better view into optimization, and would allow the JVM to optimize something speculatively and allow for Clojure tell the JVM when to deoptimize with a SwitchPoints and MutableCallSites.
Except that there's no functional mutability in Clojure! I tried hard, but I could not come up with a case in which this could be used. What are you thinking of, specifically?
Aaron
> --
> You received this message because you are subscribed to the Google
> Groups "Clojure" group.
> To post to this group, send email to clo...@googlegroups.com
> Note that posts from new members are moderated - please be patient
> with your first post.
> To unsubscribe from this group, send email to
> clojure+u...@googlegroups.com
> For more options, visit this group at
> http://groups.google.com/group/clojure?hl=en
--
/Kevin
This means I can read the rest of the logs
http://clojure-log.n01se.net/date/2008-09-03.html
and it stills hold up ? i.e we want tagged numbers ?
/Kevin
I can think of a few ways in which it would be possible to distribute a clojure.jar that supports JVM 7 features while still falling back to JVM 5 compatibility. So, I don't think this was ever a barrier. But you obviously unconvinced by my report! Thank you for challenging me on this, As I said, I'm by no means 100% sure I exhausted the issue. I would encourage you to try out your ideas.
-- If you don't provide any type hints, then the constructor for InstanceMethodExpr sets method = null and warns you that you're doing reflection...[snip]
You are right! This is perhaps the only place where invokedynamic can be useful for Clojure. *However*, to me this is not a big deal, because the workaround of allowing for type hinting solves the problem just as well, and perhaps even more efficiently from the compiler's point of view. All it does is require some extra annotations by the programmer. The fact that Clojure emits a warning seems good enough for me, personally.
-- I may be missing something, but I don't see how JRuby needs different classes.
But there is in fact another serious limitation of the JVM that comes into play here, which might seem very obvious to [Charles Nutter] but not so much to Clojure folk: the smallest container unit of code for the JVM is a class. There is no such thing as a free-floating procedure that you can just invoke whenever. ...
...This is a problem not only for JRuby, but also for implementing delegates in the Java, a long-request feature. Java thus needs to do ridiculous things like this, which in C# (for example) would be done so much more elegantly:
new Thread(new Runnable() { public void run() { ThisIsMyFakeDelegate(); }).start();
What we did above was create a new (anonymous) class as well as an instance! Clojure does essentially the same thing per fn, extending AFn instead of implementing Runnable.
* Correction: In my original report, I said that Clojure creates only instances of AFn, not classes. I was misstating the situation: it's exactly one class and one instance per fn. Clojure extends AFn and simply overrides the correct method according to arity. ...
-- Clojure has similar needs. For instance, Clojure has protocol dispatch and multimethod dispatch as well as Var based dispatch. The multimethod dispatch does a look up in the derivation hierarchy. All of this stuff can change at runtime, just like in Ruby you can add methods to a class, change methods on a class, etc. I don't see how Clojure's dispatch needs are much different than what other dynamic languages are doing.
So, perhaps I'm not understanding how you are using the term "change at runtime" to describe both JRuby regular dispatch and Clojure's multimethod. ...
Clojure's multimethod is "dynamic" in an entirely different way: the correct function is selected at runtime *according to the arguments* of the function. JVM-level linkage to the selected method would make no sense, because you'd be calling your bootstrapping method every single call!
Progress!
I am staring right now at my JVM 7 build of Clojure that uses invokedynamic for Vars that contain functions. It essentially works, well enough for experimentation. A few of my simple apps seem to work fine with it.
There are still a lot of problems: the Clojure test suite fails, and my re-linking fallback mechanism (based on SwitchPoint) does not differentiate between root bindings of Var and their "box" thread-binding, so in theory any rebinding will cause all linkages on that Var to reset. Not very optimal.
I've really done very little optimizing at all at this point: invokedynamic is just replacing the usual invokeinterface. So, I was surprised that when fooling around with some benchmarks, one of them showed about 30% performance improvement on one of my Clojure-built web servers. Tantalizing! But, until the test suite succeeds, I can't be sure that this isn't a side effect of something not so great that's going on.
I'm hoping to have something the rest of you can play with soon, but thought you would want to hear a progress report. What would be the most convenient way to share this? A dump to github of the whole tree? A patch?
I've used the Clojure 1.2.1 codebase, because I didn't want to work on a moving target, and also think it might be easier for the community to help test this with existing applications. (As I said earlier, I also have a few ideas on how to make sure we can have this mechanism gracefully downgrade to JVM <7.)
Many thanks to Paul Stadig and Charlie Nutter for prodding me to realize that the one point where invokedynamic can help is to bridge the gap between dereferencing the Var and then calling invokeinterface on it. My mistake when looking at this the first time around was assuming that the dereferencing and subsequent invokeinterface were fast operations, and that it would make little difference to replace them with invokedynamic. But, speed is not the issue here: it's that this gap represents a black hole for HotSpot, around which it can't optimize well. Because this mechanism represents the primary junction for all of Clojure, optimization here could enable some nifty optimization across the board. I think it's a good way to imagine invokedynamic's benefits: it's a bridge over troubled water for HotSpot.
Paul also points out areas where MethodHandles can replace the rather sticky reflection work that Clojure does now to massage calls to non-Clojure libraries. That's not entirely related to the invokedynamic stuff I'm working on, so I think I'll leave it to somebody else to take a stab at that...
Paul also points out areas where MethodHandles can replace the rather sticky reflection work that Clojure does now to massage calls to non-Clojure libraries. That's not entirely related to the invokedynamic stuff I'm working on, so I think I'll leave it to somebody else to take a stab at that...
I wonder if it would be that difficult to replace the reflector code with an invokedynamic. There is a way to get from a java.lang.reflect.Method to a java.lang.invoke.MethodHandle (see java.lang.invoke.MethodHandles.Lookup.unreflect). You could probably just replace the code that emits a direct call to clojure.lang.Reflector to emit an invokedynamic instruction that calls a bootstrap method that wraps the Reflector code and just converts the Method into a MethodHandle for bootstrapping.
But things always seem easier until you actually get into them. :)
I wonder if it would be that difficult to replace the reflector code with an invokedynamic. There is a way to get from a java.lang.reflect.Method to a java.lang.invoke.MethodHandle (see java.lang.invoke.MethodHandles.Lookup.unreflect). You could probably just replace the code that emits a direct call to clojure.lang.Reflector to emit an invokedynamic instruction that calls a bootstrap method that wraps the Reflector code and just converts the Method into a MethodHandle for bootstrapping.
I think you're overstating the problem: reflection is ugly, but it only happens here during compilation. From then on, the compiled code does what it needs to do with the types. At least, that's my reading of it: you told me before that I was dead wrong here, and maybe I am! If I'm right, then I don't see a "gap" here that invokedynamic can help bridge.
If you look at the links in my previous e-mail (specifically this one https://github.com/clojure/clojure/blob/master/src/jvm/clojure/lang/Compiler.java#L1364). The reflection is done in the compiler only when there is a tag (i.e. a type hint). Otherwise the compiler just emits an invoke instruction to call the Reflector at runtime. That invoke could be replaced with an invokedynamic instruction.
Well, the statement you just made is true, but nonetheless you are the
one who is mistaken. The emit() method is called, but if not enough
information was present at compile time to resolve to a real method
call (that is, if `method = null` was executed), then the emit() code
emits a call to clojure.lang.Reflector/invokeMatchingMethod, which
does runtime reflection. See
https://github.com/clojure/clojure/blob/master/src/jvm/clojure/lang/Compiler.java#L1426
where the "fast path" code is only emitted if `method != null`, and
https://github.com/clojure/clojure/blob/master/src/jvm/clojure/lang/Compiler.java#L1446
where the slow code is emitted instead.
Cheers,
Aaron Bedra
--
Clojure/core
http://clojure.com
The version of ASM that is bundled in Clojure is very old. This will likely cause problems. You are correct in looking to ASM 4 since it has started supported the JSR-292 stuff and other Java 7 changes. I am planning on doing an extraction, update, and re-packaging of ASM in Clojure as soon as Programming Clojure hits the printers. This most likely won't get started until October though because of conferences.
--
So far all I have done is update the bundled ASM and modify Clojure to emit Java7 class files, but I'm getting VerifyErrors (same as Tal). It's possible that this is a bug in the way that ASM automatically calculates stack maps, or perhaps it is the best that ASM can do, in which case the Clojure compiler would have to be modified to do bookkeeping around stack maps.