Charles Oliver Nutter schrieb: > Jochen Theodorou wrote: >> John Rose schrieb: [...] >>> Here, Groovy2ArgConverter is a hypothetical adapter-building class >>> which takes two arguments, performs conversions, and then calls a >>> target MH. The data in this class could be three MH's, one to >>> convert each of the arguments, and which is the ultimate receiver: >>> class Groovy2ArgConverter { >>> MethodHandle arg1conv, arg2conv; // called to convert the >>> incoming arguments >>> MethodHandle target; // called on the converted arguments >>> Object invoke(Object a1, Object a2) { return target.invoke >>> (arg1conv.invoke(a1), arg2conv.invoke(a2)); } >>> } >> hmm... this way I can have my runtime single argument converter methods, >> true. That's better already... still it adds a stack frame and if I have >> to convert target return value, then no tail call will help me getting >> rid of that frame I guess. I guess a common technique would then be to >> cache such adapter to be able to reuse them... well depending on class >> loader issues. > > I'd expect you can have a single argument return converter that converts > the value on the way out. Being able to convert only incoming arguments > and not outgoing values wouldn't be useful.
well, of course: > class Groovy2ArgConverter { > MethodHandle arg1conv, arg2conv; // called to convert the incoming > arguments > MethodHandle retconv; // called to convert the return value > MethodHandle target; // called on the converted arguments > Object invoke(Object a1, Object a2) { > return retconv.invoke(target.invoke(arg1conv.invoke(a1), > arg2conv.invoke(a2))); > } > } my point wasn't so much, that it is not possible, it is more that when target is invoked, the call to target is no tail call anymore now. That means the stack frame can not be removed, even if we had tail calls. retconv is in tail call position, but that won't get me much >>> This example assumes a statically linked (non-dispatching) call. If >>> there are multiple possible targets, >> which is the standard for Groovy, because we have our own dispatching >> rules, that slightly differ from Java in some cases. For example we >> allow foo(null) even if there are multiple foo methods with object >> subclassing argument. > > As does JRuby...but I think we are not formal about which method we > select. The first best match, in reflection order, I guess. we are formal, we use the most common version, for example if there is foo(Object), then it gets it... we introduced a NullObject class, but that can not be used for method parameters yet. In the long term we want to change that, so that you can define foo(null) to catch the null cases. Well, it has no priority atm [...] >>> See above an >>> example which handles all possible conversion scenarios for two >>> reference arguments; you'd need other ones for 1,3,4... argument >>> calls. If we can build flyby adapters that reify the argument lists >>> into a standard form, we can build an omnibus converter which handles >>> all kinds of argument lists. But even if we don't get those, we can >>> cope by creating N classes (where N<256 is the length of the longest >>> argument list in the system). >> Object, int, long, double, float... so for 1 argument being one of these >> and one return value being one of these types I need 25 adapter classes. >> For 2 arguments it is 125. And with three arguments I break 256 easily. > > You can put more than one signature in a given class and invoke through > it that way. well.. currently it reads as if the signature of invoke must match exactly what MethodTypes says. Which is redundant, so maybe I misunderstood it > There's no reason to generate a class for every single > permutation of arity and type. Heck, they could all be in one class with > MethodHandles to reference them. I'm not sure where the class explosion > you're talking about comes from. depends... I can not call invoke(Object) with an int unless boxing is done for me... but my goal is to not have any boxing if possible. And even if I use only double, float, int, long and Object I have already 5 types that can be combined in argument types and return types. And counting for positions I have over 500 possibilities00. 4 positions, that are 4 arguments and void/Object, or 3 arguments and return type. Or did I make a mistake? >>>> what do I need these adapter classes for? In Groovy 1.5.x we don't >>>> have >>>> them. In Groovy 1.6 we create classes at runtime, they are used for >>>> the >>>> call site caching. But the goal must be to not to need these call site >>>> classes created at runtime. I mean if I need them anyway, why do I >>>> bother with MethodHandles? > > Does Groovy still use reflection to invoke arbitrary Java methods? partially. For some GDK methods no reflection will be used, because we have pre-build stubs for them. For general methods... we still use reflection, because of possible class loader issues if we generate direct code. > Then > MethodHandles would improve performance, if nothing else. But beyond > that if you can compost the call site logic into the handles themselves, > you'll be able to eliminate them. yes, that is the goal of course ;) > I guess I'm a little confused what it is in your call site that you feel > you'd need to keep under invokedynamic. The prototype invokedynamic > support already in JRuby bypasses our call site logic entirely, > dispatching dynamically, bootstrapping, and so on. What is it about > Groovy's call site you don't think invokedynamic/method handles can help > with? I am not so much thinking about our current call sites, I am thinking about next year Groovy and trying to get the maximum potential out of this. For example in the GSOC we got a JIT compiler for Groovy, which does for example replace our call sites with almost direct method invocations. So I want to compare call sites with that JIT, not with our current call sites. Of course, since our method invocation is so slow atm, we have many places that could get tweaked a little to give more performance, but currently the method call performance makes such actions not necessary. To get good number performance one thing is essential: get rid of boxing. Even though we have introduced primitive types in our bytecode, we still have the boxing problem. The power of the JIT can be seen here: > $ export JAVA_OPTS="-server" > $ time `java -cp bin alioth.PartialSumsJ 5000000` > real 0m10.246s > user 0m0.030s > sys 0m0.015s that's the partial sum benchmark from the alioth shootout written in Java > $ time `groovy.bat test/partial_sums.groovy 5000000` > real 0m27.110s > user 0m0.030s > sys 0m0.016s > > $ time `groovy.bat test/alioth/PartialSums.groovy 5000000` > real 0m28.409s > user 0m0.030s > sys 0m0.015s these are variants written in Java using Groovy 1.6 > > $ export JAVA_OPTS="-server > -javaagent:C:\\groovy-ck1\\healthy\\target\\install\\lib\\gjit-0.1.jar" > $ time `groovy.bat test/alioth/PartialSums.groovy 5000000` > real 0m13.434s > user 0m0.030s > sys 0m0.030s and that's the JIT version. This does in no way reflect arbitrary method calls for 1.6, but the JIT version does. And while a groovy method call can currently grow to being 30 times slower, a JIT version can be better than factor 2. invokedynamic needs to be measured at this bye blackdrag -- Jochen "blackdrag" Theodorou The Groovy Project Tech Lead (http://groovy.codehaus.org) http://blackdragsview.blogspot.com/ http://www.g2one.com/ --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "JVM Languages" group. To post to this group, send email to jvm-languages@googlegroups.com To unsubscribe from this group, send email to [EMAIL PROTECTED] For more options, visit this group at http://groups.google.com/group/jvm-languages?hl=en -~----------~----~----~----~------~----~------~--~---