I'm pondering how Kawa can make use of MethodHandles without (yet) going
the full invokedynamic route.  Specifically, I'm considering using
MethodHandles in place of virtual methods, and I'm hoping someone has
some information on the likely costs and benefits.

The attachment ProcUsingVirt.java sketches out the current implementation.
This uses virtual apply[01N] methods that are overridden by specific
Procedure subclasses. (Kawa actually has apply[01234N], but I've simplified.)

(See also http://www.gnu.org/software/kawa/internals/procedures.html)

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.

The first issue is that ProcUsingMH requires more fields, and thus more memory.
This is mitigated because we don't need space for the virtual method tables.
Because Procedure classes are often singletons, this may be a wash.

Calling an MethodHandle using invokeExact requires more indirection and
more checking than just doing an invokevirtual.  Would Hotspot be able to
optimize this to roughly the same speed?  Presumably it might be a little
bit slower on less-optimizing VMs.

To avoid one-class-per-function, Kawa will compile a switch-table per module,
as sketched by the ProcUsingVirt.Builtins2 class.  (see the section named
"Closures" in the internals document linked to above.)  This is where using
MethodHandles seems most winning, since we can do as shown in the
ProcUingMH.Builins class.  Is this likely to have a performance impact?

What if the apply[01N] method is non-final? That might make interoperability
easier.  For example, it's desirable to support a '--target 6' flag, though
supporting both styles in the same runtime may reduce the advantages of either.

Finally, it seems that if Kawa in the future makes use of invokedynamic,
having the MethodHandles in the Procedure would be an advantage.

Comments?  Advice?
--
        --Per Bothner
p...@bothner.com   http://per.bothner.com/
abstract class Procedure {
    final String name;

    protected Procedure(String name) {
        this.name = name;
    }

    /** Call this procedure with 0 arguments. */
    public abstract Object apply0() throws Throwable;
    /** Call this procedure with 1 arguments. */
    public abstract Object apply1(Object arg1) throws Throwable;
    /** Call this procedure with an argument list. */
    public abstract Object applyN(Object[] args) throws Throwable;
}

/** A 1-argument Procedure. */
abstract class Procedure1 extends Procedure {
    public Procedure1(String name) {
        super(name);
    }
    public Object apply0() throws Throwable {
        throw new RuntimeException();
    }
    public Object applyN(Object[] args) throws Throwable {
        if (args.length != 1) throw new RuntimeException();
        return apply1(args[0]);
    }
}

/** Create one singleton class for each Procedure. */
class Builtins1 {
    static final Procedure hash = new Procedure1("hash") {
            public Object apply1(Object arg1) throws Throwable {
                return arg1.hashCode();
            }
        };
}

/** Use a switch to share a single class for more than one Procedure. */
class Builtins2 extends Procedure1 {
    static final int HASH_SWITCH_INDEX = 1;
    int switchIndex;
    static final Procedure hash = new Builtins2("hash", HASH_SWITCH_INDEX);
    Builtins2(String name, int switchIndex) {
        super(name);
        this.switchIndex = switchIndex;
    }

    public Object apply1(Object arg1) throws Throwable {
        switch (switchIndex) {
        case HASH_SWITCH_INDEX:
            return arg1.hashCode();
        default:
            throw new RuntimeException();
        }
    }
}
import java.lang.invoke.*;

abstract class Procedure {
    final String name;
    final MethodHandle mh0; // Used for 0-argument calls.
    final MethodHandle mh1; // Used for 1-argument calls.
    final MethodHandle mhn; // Used for other calls.

    protected Procedure(String name,
                        MethodHandle mh0,
                        MethodHandle mh1,
                        MethodHandle mhn) {
        this.name = name;
        this.mh0 = mh0;
        this.mh1 = mh1;
        this.mhn = mhn;
    }
    /** Call this procedure with 0 arguments. */
    public final Object apply0() throws Throwable {
        return mh1.invokeExact(this);
    }
    /** Call this procedure with 1 arguments. */
    public final Object apply1(Object arg1) throws Throwable {
        return mh1.invokeExact(this, arg1);
    }
    /** Call this procedure with an argument list. */
    public final Object applyN(Object[] args) throws Throwable {
        return mh1.invokeExact(this, args);
    }

    protected static MethodHandles.Lookup lookup = MethodHandles.lookup();
    protected static MethodHandle findStatic(Class<?> refc,
                                              String name,
                                              MethodType type) {
        try {
            return lookup.findStatic(refc, name, type);
        } catch (Throwable ex) {
            throw new RuntimeException(ex);
        }
    }
    protected static final MethodHandle apply0Error =
        findStatic(Procedure.class, "apply0Error",
                   MethodType.methodType(Object.class, Procedure.class));
    protected static final MethodHandle apply1Error =
        findStatic(Procedure.class, "apply1Error",
                   MethodType.methodType(Object.class, Procedure.class, Object.class));
    protected static final MethodHandle applyNError =
        findStatic(Procedure.class, "applyNError",
                   MethodType.methodType(Object.class, Procedure.class, Object[].class));
    public static Object apply0Error(Procedure proc) {
        throw new RuntimeException();
    }
    public static Object apply1Error(Procedure proc, Object arg1) {
        throw new RuntimeException();
    }
    public static Object applyNError(Procedure proc, Object[] args) {
        throw new RuntimeException();
    }
}

/** A 1-argument Procedure. */
class Procedure1 extends Procedure {
    public Procedure1(String name, MethodHandle mh1) {
        super(name, Procedure.apply0Error, mh1, Procedure.applyNError);
    }

    public static Object applyNDefault(Procedure proc, Object[] args)
        throws Throwable {
        if (args.length != 1) throw new RuntimeException();
        return proc.mh1.invokeExact(proc, args[0]);
    }
    static final MethodHandle applyNDefault =
        findStatic(Procedure1.class, "applyNDefault",
                   MethodType.methodType(Object.class, Procedure.class, Object[].class));
}

class Builtins {
    public static final Object hashPrim(Procedure proc, Object arg1) {
        return arg1.hashCode();
    }

    static final Procedure hash = new Procedure1("hash",
                                                Procedure.findStatic(Builtins.class, "hashPrim",
                                                                     MethodType.methodType(Object.class, Procedure.class, Object.class)));
}
_______________________________________________
mlvm-dev mailing list
mlvm-dev@openjdk.java.net
http://mail.openjdk.java.net/mailman/listinfo/mlvm-dev

Reply via email to