using MethodHandle in place of virtual methods?

Rémi Forax forax at univ-mlv.fr
Sat May 19 05:02:49 PDT 2012


On 05/19/2012 08:04 AM, Per Bothner wrote:
> On 05/16/2012 05:52 PM, Charles Oliver Nutter wrote:
>
> Thanks for a detailed and helpful response!
>
>> On Wed, May 16, 2012 at 12:55 PM, Per Bothner<per at bothner.com>   wrote:
>>> The attachment ProcUsingMH.java is a sketch of an alternative implementation
>>> where each apply[01N] method has a matching MethodHandle field.  The
>>> apply[01N]
>>> method is now final and just invokes the corresponding MethodHandle.
>> I don't think this would optimize like you're hoping.
> I'm not expecting a big performance gain.
> I'm hoping for a slight gain getting rid of using a dispatch-switch,
> partly because it avoids an extra indirection (and virtual dispatch), and
> partly because (as you mentioned) HotSpot may have trouble
> optimizing switches, at least large ones.
>
> To summarize my questions, before I actually try to implement this:
> (1) A change to replace a virtual applyX method by a final method
> that calls a InvokeMethod in a final field: My guess is this would
> performancewise be more-or-less a wash, with neither major gain
> or less.  Is that likely?
> (2) My guess is using the new MethodHandle scheme might be slighly
> more efficient than using a switch on a "procedure-index".  It be
> be significantly more efficient in cases of big switch.  Is that likely?
>
> I think there may be additional benefits to getting rid of the
> dispatch-switch: More helpful exception stack traces;
> plus avoiding the need to generate the dispatch classes.
>
>>> Finally, it seems that if Kawa in the future makes use of invokedynamic,
>>> having the MethodHandles in the Procedure would be an advantage.
>> We do keep a method handle in each method object, but only so we can
>> more easily retrieve it and bind it to the invokedynamic call site. I
>> don't think your'e going to see the performance gain you're hoping for
>> with the Procedure object that aggregates handles. You really need the
>> invokedynamic call site for the whole thing to optimize together well.
> It probably makes sense to not implement a switch to using MethodHandles
> until I have a design (roadmap) for using invokedynamic, since the
> benefit of the former change is at best modest and probably not worth the
> effort unless Kawa also makes use of invokedynamic.  Does that match
> your advice?

As Charles said, you will not see big gain to use method handle
until you use invokedynamic, but I think it worth a test.

Here a version of Procedure that doesn't use inheritance at all
and that you be more or less backward compatible with code
that are already compiled with Procedure.

import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodHandles.Lookup;
import java.lang.invoke.MethodType;

class Procedure {
     public final MethodHandle mh;   // Used for classical call.
     private MethodHandle varargsMH; // Used for varargs call, allocated 
lazily

     Procedure(MethodHandle mh) {
         this.mh = mh;
     }

     /** backward compatibility. */
     public final Object apply0() throws Throwable {
         if (mh.type().parameterCount() != 0)
             throw new RuntimeException();
         return mh.invokeExact();
     }
     /** backward compatibility. */
     public final Object apply1(Object arg1) throws Throwable {
         if (mh.type().parameterCount() != 1)
             throw new RuntimeException();
         return mh.invokeExact(arg1);
     }
     /** backward compatibility. */
     public final Object applyN(Object[] args) throws Throwable {
         MethodHandle varargsMH = this.varargsMH;
         if (varargsMH == null) {
            varargsMH = mh.asSpreader(Object[].class, 
mh.type().parameterCount());
         }
         return varargsMH.invokeExact(args);
     }
}

class Builtins {
     public static final Object hash(Object arg1) {
         return arg1.hashCode();
     }

     private static Procedure createProcedure(Lookup lookup, String 
name, int parameterCount) {
       MethodHandle mh;
       try {
         mh = lookup.findStatic(Builtins.class, name, 
MethodType.genericMethodType(parameterCount));
       } catch (NoSuchMethodException | IllegalAccessException e) {
         throw (LinkageError)new LinkageError().initCause(e);
       }
       return new Procedure(mh);
     }

     static final Procedure hash;

     static {
       Lookup lookup = MethodHandles.lookup();
       hash = createProcedure(lookup, "hash", 1);
     }
}

class Main {
   public static void main(String[] args) throws Throwable {
     // new way, get the method handle and call
     System.out.println(Builtins.hash.mh.invokeExact((Object)"foo"));

     // old way - apply1
     System.out.println(Builtins.hash.apply1("foo"));

     // old way - applyN
     System.out.println(Builtins.hash.applyN(new Object[]{"foo"}));
   }
}


cheers,
Rémi



More information about the mlvm-dev mailing list