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