Few questions about invokeDynamic

assembling signals assembling.signals at yandex.ru
Mon Nov 8 11:52:21 PST 2010


Ok, I extended my "benchmark" to support a method handle with spread args,
and this part slower than everything else :(

Note: I use Object[] for spread arguments, because in my real-world scenario,
as mentioned before, I would need any possible method signature to work.

Sadly, I still couldn't figure out, what John meant with
"calling Lookup.findVirtual on MethodHandle.invokeExact",
and I wonder if it would speed things up.

= = = = = = = = = =
CODE
= = = = = = = = = =

import java.lang.reflect.*;
import java.text.*;
import java.dyn.*;

/*
 * CAN ONLY BE COMPILED AND RAN OUTSIDE NETBEANS
 */
public class _BenchmarkDyn_onefile {

	static abstract class Test {

		final String name;

		Test(String name) {
			this.name = name;
		}

		abstract void run()
				throws Throwable;

	}

	private final static int N_REPEATS = 10;
	private final static long INNER_LOOP = 5 * 1000 * 1000;
	private static long counter = 0;
	private final static Object[] CACHED_ARGS_ARRAY = new Object[2];
	private final static Method method;
	private final static MethodHandle mhNormal;
	private final static MethodHandle mhSpread;
	private volatile static int DEOPTIMIZED_INT = System.getProperties().size();

	public static void staticMethod(int i1, int i2) {
		counter += i1;
		counter += i2;
	}

	private static int deoptimizedInt() {
		return DEOPTIMIZED_INT;
	}

	static {
		try {
			method = _BenchmarkDyn_onefile.class.getMethod(
					"staticMethod", int.class, int.class);
			Linkage.registerBootstrapMethod("bootstrap");
			mhNormal = MethodHandles.lookup().findStatic(
					_BenchmarkDyn_onefile.class, "staticMethod",
					MethodType.methodType(void.class, int.class, int.class));
			mhSpread = MethodHandles.spreadArguments(mhNormal,
					MethodType.methodType(void.class, Object[].class));
		} catch (Exception ex) {
			throw new Error(ex);
		}
	}

	private static CallSite bootstrap(Class<?> declaring, String name, MethodType type) {
		System.out.println(declaring + "." + name + " : " + type);
		CallSite cs = new CallSite();
		cs.setTarget(mhNormal);
		return cs;
	}

	private static void doUsual() {
		if (deoptimizedInt() >= 0) {
			staticMethod(-1, +2);
		}
	}

	private static void doDynSyntax()
			throws Throwable {
		if (deoptimizedInt() >= 0) {
			InvokeDynamic.anyName((int) -1, (int) +2);
		}
	}

	private static void doDynExact()
			throws Throwable {
		if (deoptimizedInt() >= 0) {
			mhNormal.invokeExact((int) -1, (int) +2);
		}
	}

	private static void doDynVarargs()
			throws Throwable {
		if (deoptimizedInt() >= 0) {
			// bug: when using on a M.H. without args, null is allowed for invokeVarargs, but fails
			CACHED_ARGS_ARRAY[0] = -1;
			CACHED_ARGS_ARRAY[1] = 2;
			mhNormal.invokeVarargs(CACHED_ARGS_ARRAY);
		}
	}

	private static void doDynSpread()
			throws Throwable {
		if (deoptimizedInt() >= 0) {
			CACHED_ARGS_ARRAY[0] = -1;
			CACHED_ARGS_ARRAY[1] = 2;
			// invokeExact and invokeGeneric both equally "fast"
			mhSpread.invokeExact(CACHED_ARGS_ARRAY);
		}
	}

	private static void doReflect()
			throws Throwable {
		if (deoptimizedInt() >= 0) {
			CACHED_ARGS_ARRAY[0] = -1;
			CACHED_ARGS_ARRAY[1] = 2;
			method.invoke(null, CACHED_ARGS_ARRAY);
		}
	}

	private static void tests(Test[] tt) {
		DecimalFormat format = new DecimalFormat("#0.00");
		System.out.println("wait...\n");

		double[] times = new double[tt.length];
		for (int t = 0; t < N_REPEATS; ++t) {
			for (int i = 0; i < tt.length; ++i) {
				times[i] += test(tt[i]);
			}
			System.out.print(".");
			System.out.flush();
		}

		System.out.println("done!\n");
		for (int i = 0; i < tt.length; ++i) {
			System.out.println(tt[i].name + "\n\t" + format.format(times[i]) + "s");
		}
	}

	private static double test(Test t) {
		counter = 0;
		long start = System.currentTimeMillis();
		try {
			t.run();
		} catch (Throwable thr) {
			throw new Error(thr);
		}
		long end = System.currentTimeMillis();
		if (counter != INNER_LOOP) {
			throw new AssertionError("counter != " + INNER_LOOP);
		}
		return (end - start) / 1000.0;
	}

	public static void main(String... args) {
		Test[] tt = new Test[]{
			new Test("usual method invocation") {

				@Override
				void run()
						throws Throwable {
					for (long i = 0; i < INNER_LOOP; ++i) {
						doUsual();
					}
				}

			},
			new Test("invoke dynamic: using InvokeDynamic.anyName") {

				@Override
				void run()
						throws Throwable {
					for (long i = 0; i < INNER_LOOP; ++i) {
						doDynSyntax();
					}
				}

			},
			new Test("invoke dynamic: using invokeExact with real args") {

				@Override
				void run()
						throws Throwable {
					for (long i = 0; i < INNER_LOOP; ++i) {
						doDynExact();
					}
				}

			},
			new Test("invoke dynamic: using invokeVarargs") {

				@Override
				void run()
						throws Throwable {
					for (long i = 0; i < INNER_LOOP; ++i) {
						doDynVarargs();
					}
				}

			},
			new Test("invoke dynamic: using invokeExact with spread args array") {

				@Override
				void run()
						throws Throwable {
					for (long i = 0; i < INNER_LOOP; ++i) {
						doDynSpread();
					}
				}

			},
			new Test("reflection: using Method.invoke(...)") {

				@Override
				void run()
						throws Throwable {
					for (long i = 0; i < INNER_LOOP; ++i) {
						doReflect();
					}
				}

			} };

		tests(tt);
	}

}

= = = = = = = = = =
OUTPUT
= = = = = = = = = =
usual method invocation
        0.10s
invoke dynamic: using InvokeDynamic.anyName
        0.12s
invoke dynamic: using invokeExact with real args
        0.79s
invoke dynamic: using invokeVarargs
        8.72s
invoke dynamic: using invokeExact with spread args array
        15.20s
reflection: using Method.invoke(...)
        0.82s


More information about the mlvm-dev mailing list