This is the best summary of how Clojure *could* benefit from invokedynamic.

Clojure doesn't dispatch as dynamically as JRuby, but there is a dynamic 
component...specifically, you have to go get the fn. That repeated "get" 
would disappear with invokedynamic,

In fact, anywhere you're going after something that's immutable but requires 
a memory access, you could potentially use invokedynamic to eliminate repeat 
memory accesses. I assume that would help in many places.

Also, as Paul notes below, there's no need for multiple-arity interfaces to 
get full-speed perf if you use invokedynamic; JRuby's invokedynamic-based 
dispatch ends up going directly from call site to target code body, with no 
intervening interface at all.

Listen to Paul. He's right.

- Charlie

On Friday, August 26, 2011 5:37:53 AM UTC-5, Paul Stadig wrote:
>
> On Thu, Aug 25, 2011 at 5:41 PM, Tal Liron <tal....@gmail.com> wrote:
>
>> 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]
>>
>
> What I said in another post was that with invokedynamic (assuming you could 
> just go whole hog into Java 7) you don't need the IFn interface. You just 
> need a MethodHandle. So yes, I agree that given all the existing Clojure 
> code, and the desire to stay compatible with Java 5 or 6 or whatever, 
> invokedynamic isn't very compelling. If you were going to do a green field 
> project, and didn't mind the Java 7 requirement, invokedynamic is very 
> compelling, because you can basically push a bunch of code down into the 
> JVM. This mean much less work, and it means you get to benefit from any 
> improvements and optimizations that get built into the JVM. Even for Clojure 
> having an optional Java 7 target may still be interesting in that it allows 
> you to hook into all the work that will be done on invokedynamic to support 
> the whole dynamic language ecosystem on the JVM.
>  
>
>> 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.
>>
>
> It's very possible that I'm missing something, but I'm not quite sure what 
> you're getting at here. In JRuby, for instance, the method overloading is 
> the same as in Clojure, it is based on arity. The slowdown is in dispatch 
> where you have to check a singleton class, your class, your ancestors, then 
> method_missing, etc. Once you've looked this all up you cache it to speed it 
> up later. Then you want to invalidate the cache when the world changes.
>
> 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.
>  
>
>> 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.
>>
>
> The "signature" of a JRuby method is not much different than a Clojure 
> method. It is an arity overloaded method with generic arguments. They do 
> similar things to lots of other dynamic languages (including Clojure) like 
> generating a class with a bunch of different versions of the same method 
> with different numbers of generic arguments (
> http://jruby.org/git?p=jruby.git;a=blob;f=src/org/jruby/internal/runtime/methods/DynamicMethod.java;h=b65854c057c8a1acec57d6dad95158f08c960dcc;hb=HEAD#l203).
>  
> invokedynamic isn't about method signatures it's about dynamic dispatch, 
> which is what Clojure does.
>  
>
>> 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.]
>>
>
> This just isn't true. If you don't provide any type hints, then the 
> constructor for InstanceMethodExpr sets method = null and warns you that 
> you're doing reflection (if you have the warn-on-reflection var set) 
> https://github.com/clojure/clojure/blob/master/src/jvm/clojure/lang/Compiler.java#L1364then
>  in the emit function, when method == null it compiles the invocation to 
> a call to call to Reflector.invokeInstanceMethod (
> https://github.com/clojure/clojure/blob/master/src/jvm/clojure/lang/Compiler.java#L1456and
>  
> https://github.com/clojure/clojure/blob/master/src/jvm/clojure/lang/Reflector.java#L25)
>  
> which does reflection at runtime everytime you invoke the 
> method...*everytime*. With invokedynamic you can do the lookup in a 
> bootstrap method for the invokedynamic call site, you get a MethodHandle to 
> the method you found, and you cache it in a CallSite. Then the next time you 
> come through you just use the MethodHandle in the CallSite. That's a huge 
> performance boost. With invokedynamic you can do the same thing for static 
> field access. Clojure cannot optimize static field access like this.
>
> Also the Clojure Reflector class does a lot of mojo to try to find the 
> right method, and do boxing of arguments to match them up with the method 
> you want to invoke (
> https://github.com/clojure/clojure/blob/master/src/jvm/clojure/lang/Reflector.java#L467and
>  
> https://github.com/clojure/clojure/blob/master/src/jvm/clojure/lang/Reflector.java#L427).
>  
> If you call MethodHandle.invoke (versus MethodHandle.invokeExact) it does 
> that for you automatically, and presumably in the lower layers of the JVM 
> that Clojure cannot access, *and* the JVM knows more about what is going on 
> for optimization purposes.
>
> Clojure also does other things with arguments to functions, like collecting 
> and spreading rest args. These can be done with a chain of MethodHandles 
> which pushes the work down into the JVM.
>  
>
>> [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.
>>
>
> Not everything in Clojure is immutable. The most basic element of Clojure's 
> dispatch (the Var) is mutable, and Clojure could benefit in the same way as 
> JRuby with SwitchPoints. Rich did work in 1.3 to basically keep track of 
> when the world of Vars changes, and when it does all of the cached Var 
> values get invalidated, and the next time a Var is used its value is 
> re-fetched. This is basically a SwitchPoint mechanism, and Sun nee Oracle 
> has already done the work for you, and other dynamic languages will be using 
> it, and everyone will benefit from improvements and optimizations except 
> Clojure, because we don't use invokedynamic.
>  
>
>> In summary, Clojure's dynamics are already handled as well as could be on 
>> the JVM, at least as far as I can see.
>>
>
> For version 5 of the JVM, I agree, and if your goal is to maintain 
> compatibility with version 5 of the JVM, sure. However, there is lots of 
> invokedynamic goodness that Clojure could benefit from. Java 7 could be an 
> optional target that would tap into invokedynamic. You then get into the 
> debate about whether its going to be worth the effort, when what we have 
> already is about as good as you can do on Java 5.
>
> I'm not necessarily disagreeing with anyone who says it doesn't make sense 
> for Clojure to support invokedynamic given the current situation and desire 
> to maintain Java 5 compatibility. However, I do take issue with people 
> saying that Clojure cannot benefit from invokedynamic in principle.
>
>
> Paul
>
>

-- 
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clojure@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+unsubscr...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en

Reply via email to