This is an automated email from the ASF dual-hosted git repository. blackdrag pushed a commit to branch feature/indy_perf in repository https://gitbox.apache.org/repos/asf/groovy.git
commit b59f0b73dd4b2257009c18a65f22958054e494ba Author: Jochen Theodorou <[email protected]> AuthorDate: Mon Sep 11 12:49:20 2023 +0200 some initial performance testing infrastructure to compare indy with other solutions --- src/test/indy/perf/IndyDirectTest.java | 73 ++++++++ src/test/indy/perf/IndyDoubleDispatchTest.java | 86 +++++++++ src/test/indy/perf/IndyPerfUtil.java | 238 +++++++++++++++++++++++++ src/test/indy/perf/IndyPromotionTest.java | 95 ++++++++++ src/test/indy/perf/RuntimeCallsiteGenTest.java | 93 ++++++++++ src/test/indy/perf/SimpleReflectiveTest.java | 94 ++++++++++ src/test/indy/perf/StaticCallsiteGenTest.java | 91 ++++++++++ 7 files changed, 770 insertions(+) diff --git a/src/test/indy/perf/IndyDirectTest.java b/src/test/indy/perf/IndyDirectTest.java new file mode 100644 index 0000000000..984a0cc239 --- /dev/null +++ b/src/test/indy/perf/IndyDirectTest.java @@ -0,0 +1,73 @@ +package indy.perf; + +import org.junit.FixMethodOrder; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TestRule; +import org.junit.rules.TestWatcher; +import org.junit.runner.Description; +import org.junit.runners.MethodSorters; +import org.objectweb.asm.Opcodes; + +import java.io.IOException; +import java.lang.invoke.*; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.Comparator; + +import static indy.perf.IndyPerfUtil.*; + +@FixMethodOrder( MethodSorters.NAME_ASCENDING ) +public class IndyDirectTest { + @Rule + public final TestRule watchman = new TestWatcher() { + @Override + protected void starting(Description description) { + System.out.println(description + "\n\n\tstarted."); + } + + @Override + protected void finished(Description description) { + System.out.println(description + "\tdone."); + } + }; + + public static int foo(int i) { + return i; + } + + @SuppressWarnings("unused") + public static CallSite bootstrap(MethodHandles.Lookup caller, String name, MethodType type) { + MethodHandle handle; + try { + handle = caller.findStatic(IndyDirectTest.class, name, type); + } catch (NoSuchMethodException | IllegalAccessException e) { + throw new RuntimeException(e); + } + return new ConstantCallSite(handle); + } + + @Test + public void t1_indyDirectCall1() throws ReflectiveOperationException, IOException { + execute(null); + } + + @Test + public void t2_indyDirectCall2() throws ReflectiveOperationException, IOException { + execute("t2_indyDirectCall2"); + } + + public void execute(String name) throws ReflectiveOperationException, IOException { + Class<? extends Runnable> c = writeIndyCall(getBSM(IndyDirectTest.class).handle, "foo", "(I)I", (mv -> { + mv.visitInsn(Opcodes.ICONST_1); + })); + Runnable r = c.getDeclaredConstructor().newInstance(); + perf(r, name); + } + + public static void main(String[] args) { + executeTests(IndyDirectTest.class); + } + +} diff --git a/src/test/indy/perf/IndyDoubleDispatchTest.java b/src/test/indy/perf/IndyDoubleDispatchTest.java new file mode 100644 index 0000000000..c49d84d15c --- /dev/null +++ b/src/test/indy/perf/IndyDoubleDispatchTest.java @@ -0,0 +1,86 @@ +package indy.perf; + +import org.junit.FixMethodOrder; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TestRule; +import org.junit.rules.TestWatcher; +import org.junit.runner.Description; +import org.junit.runners.MethodSorters; +import org.objectweb.asm.Opcodes; + +import java.io.IOException; +import java.lang.invoke.*; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.Comparator; + +import static indy.perf.IndyPerfUtil.*; + +@FixMethodOrder( MethodSorters.NAME_ASCENDING ) +public class IndyDoubleDispatchTest { + @Rule + public final TestRule watchman = new TestWatcher() { + @Override + protected void starting(Description description) { + System.out.println(description + "\n\n\tstarted."); + } + + @Override + protected void finished(Description description) { + System.out.println(description + "\tdone."); + } + }; + + public static int foo(int i) { + return i; + } + + private static MethodHandle SELECTOR = getHandle(IndyDoubleDispatchTest.class, "selector"); + + @SuppressWarnings("unused") + public static CallSite bootstrap(MethodHandles.Lookup caller, String name, MethodType type) { + MutableCallSite callsite = new MutableCallSite(type); + MethodHandle handle = MethodHandles.insertArguments(SELECTOR, 0, caller, callsite, name); + callsite.setTarget(handle); + return callsite; + } + + @SuppressWarnings("unused") + public static int selector(MethodHandles.Lookup caller, MutableCallSite callsite, String name, int i) { + MethodHandle handle; + try { + handle = caller.findStatic(IndyDoubleDispatchTest.class, name, callsite.type()); + } catch (NoSuchMethodException | IllegalAccessException e) { + throw new RuntimeException(e); + } + callsite.setTarget(handle); + try { + return (int) handle.invokeExact(i); + } catch (Throwable e) { + throw new RuntimeException(e); + } + } + + @Test + public void t1_indyDoubleDispatch() throws ReflectiveOperationException, IOException { + execute(null); + } + + @Test + public void t2_indyDoubleDispatch() throws ReflectiveOperationException, IOException { + execute("t2_indyDoubleDispatch"); + } + + public void execute(String name) throws ReflectiveOperationException, IOException { + Class<? extends Runnable> c = writeIndyCall(getBSM(IndyDoubleDispatchTest.class).handle, "foo", "(I)I", (mv -> { + mv.visitInsn(Opcodes.ICONST_1); + })); + Runnable r = c.getDeclaredConstructor().newInstance(); + perf(r, name); + } + public static void main(String[] args) { + executeTests(IndyDoubleDispatchTest.class); + } +} diff --git a/src/test/indy/perf/IndyPerfUtil.java b/src/test/indy/perf/IndyPerfUtil.java new file mode 100644 index 0000000000..aa9bf02175 --- /dev/null +++ b/src/test/indy/perf/IndyPerfUtil.java @@ -0,0 +1,238 @@ +package indy.perf; + +import org.junit.Test; +import org.objectweb.asm.ClassWriter; +import org.objectweb.asm.Handle; +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Opcodes; + +import java.io.*; +import java.lang.invoke.CallSite; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.Arrays; +import java.util.Comparator; +import java.util.Optional; +import java.util.function.Consumer; + +import static org.objectweb.asm.Opcodes.ACC_PUBLIC; + +public class IndyPerfUtil { + private static final int WARM_UP_ITERATIONS = 100; + private static final int TEST_ITERATIONS = 10; + private static final int HOT_LOOP_ITERATIONS = 100_000; + public static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup(); + private static boolean WRITE_FILE = true; + + private static long getTotal(long[] times, int length) { + long t_diff = 0; + for (int i=0; i<length; i+=2) { + t_diff += times[i+1] - times[i]; + } + return t_diff; + } + + public static void perf(Runnable r, String name) { + System.out.println("------------------------------------"); + long[] warmupTimes = new long[WARM_UP_ITERATIONS*2]; + long time0 = System.nanoTime(); + r.run(); + long time1 = System.nanoTime(); + warmupTimes[0] = time0; + warmupTimes[1] = time1; + print("init time", time0, time1, 1, 0); + + for (int i = 0; i< WARM_UP_ITERATIONS-1; i++) { + long w1 = System.nanoTime(); + r.run(); + long w2 = System.nanoTime(); + int index = (i+1)*2; + warmupTimes[index] = w1; + warmupTimes[index+1] = w2; + print("warmup time per iteration", w1, w2, 1, getTotal(warmupTimes, index+2)); + } + System.out.println("------------------------------------"); + long t_diff = getTotal(warmupTimes, warmupTimes.length); + System.out.println("total warmup time: " + t_diff); + + if (name != null && WRITE_FILE) { + try(FileOutputStream fo = new FileOutputStream("perf.data", true)) { + PrintWriter pw = new PrintWriter(fo); + pw.println(); + pw.println(name); + long tdiff = 0; + for (int i=0; i<warmupTimes.length; i+=2) { + long diff = warmupTimes[i+1]-warmupTimes[i]; + tdiff += diff; + pw.print(diff); + pw.print('\t'); + pw.println(tdiff); + } + pw.println(); + pw.flush(); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + + System.out.println("------------------------------------"); + for (int j = 0; j< TEST_ITERATIONS; j++) { + long timeStart = System.nanoTime(); + for (int i = 0; i < HOT_LOOP_ITERATIONS; i++) { + r.run(); + } + long timeEnd = System.nanoTime(); + print("avg time in iteration " + j, timeStart, timeEnd, HOT_LOOP_ITERATIONS, 0); + } + System.out.println("------------------------------------"); + } + + public static Class<? extends Runnable> writeIndyCall( + Handle bootstrap, String message, String messageSignature, Consumer<MethodVisitor> argWriter + ) throws IOException { + ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES); + cw.visit( + Opcodes.V17, Opcodes.ACC_SUPER | Opcodes.ACC_PUBLIC, + "indy/Runner", + null, "java/lang/Object", new String[]{Runnable.class.getName().replace('.', '/')} + ); + + MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PUBLIC, "run", "()V", null, new String[0]); + argWriter.accept(mv); + mv.visitInvokeDynamicInsn(message, messageSignature, bootstrap); + mv.visitInsn(Opcodes.POP); + mv.visitInsn(Opcodes.RETURN); + mv.visitMaxs(0, 0); + mv.visitEnd(); + + mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null); + mv.visitIntInsn(Opcodes.ALOAD, 0); + mv.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false); + mv.visitInsn(Opcodes.RETURN); + mv.visitMaxs(0, 0); + mv.visitEnd(); + + cw.visitSource("Runner.dyn", null); + cw.visitEnd(); + byte[] bytes = cw.toByteArray(); + try(FileOutputStream fos = new FileOutputStream("Runner.class")){ + fos.write(bytes); + fos.flush(); + } + return makeClass(bytes); + } + + @SuppressWarnings("unchecked") + private static <T> Class<T> makeClass(byte[] code) { + //return (Class<? extends Runnable>) LOOKUP.defineClass(code); + var cl = new ClassLoader(IndyPerfUtil.class.getClassLoader()) { + public Class<? extends Runnable> makeClass() { + return (Class<? extends Runnable>) defineClass(null, code, 0, code.length); + } + }; + return (Class<T>) cl.makeClass(); + } + + private static void print(String text, long t1, long t2, int runs, long totalTime) { + System.out.print(text + " with runs " + runs +": (ns) " + (t2-t1)/runs); + if (totalTime>0) { + System.out.print(" / total "+totalTime+" (ns)"); + } + System.out.println(); + } + + public static BSM getBSM(Class<?> clazz) { + Optional<Method> method = Arrays.stream(clazz.getMethods()). + filter(m -> m.getName().equals("bootstrap")). + filter(m -> Modifier.isStatic(m.getModifiers())). + findFirst(); + if (method.isEmpty()) { + throw new AssertionError("cannot find static bootstrap method"); + } + MethodType type = MethodType.methodType( + CallSite.class, MethodHandles.Lookup.class, String.class, MethodType.class + ); + + Handle handle = new Handle(Opcodes.H_INVOKESTATIC, + clazz.getName().replace('.', '/'), "bootstrap", type.toMethodDescriptorString(), false + ); + return new BSM(handle, method.get()); + } + + public static MethodHandle getHandle(Class<?> clazz, String name) { + Optional<Method> method = Arrays.stream(clazz.getMethods()). + filter(m -> m.getName().equals(name)). + findFirst(); + try { + return LOOKUP.unreflect(method.orElseThrow()); + } catch (IllegalAccessException iae){ + throw new AssertionError(iae); + } + } + + public static <T> Class<T> writeCallSiteImpl(Class<?> callsiteGen, Class<?> fooOwner) { + ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES); + cw.visit( + Opcodes.V17, Opcodes.ACC_SUPER | Opcodes.ACC_PUBLIC, + "callsite/Runner", + null, "java/lang/Object", new String[]{callsiteGen.getName().replace('.', '/')} + ); + + MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PUBLIC, "call", "([Ljava/lang/Object;)Ljava/lang/Object;", null, new String[0]); + mv.visitIntInsn(Opcodes.ALOAD, 1); + mv.visitInsn(Opcodes.ICONST_0); + mv.visitInsn(Opcodes.AALOAD); + mv.visitTypeInsn(Opcodes.CHECKCAST, "java/lang/Integer"); + mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Integer", "intValue", "()I", false); + mv.visitMethodInsn(Opcodes.INVOKESTATIC, fooOwner.getName().replace('.', '/'), "foo", "(I)I", false); + mv.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/Integer", "valueOf", "(I)Ljava/lang/Integer;", false); + mv.visitInsn(Opcodes.ARETURN); + mv.visitMaxs(0, 0); + mv.visitEnd(); + + mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null); + mv.visitIntInsn(Opcodes.ALOAD, 0); + mv.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false); + mv.visitInsn(Opcodes.RETURN); + mv.visitMaxs(0, 0); + mv.visitEnd(); + + cw.visitSource("CallsiteImpl.dyn", null); + cw.visitEnd(); + byte[] bytes = cw.toByteArray(); + return makeClass(bytes); + } + + public static void executeTests(Class<?> clazz) { + try { + final Object instance = clazz.getDeclaredConstructor().newInstance(); + + Arrays.stream(clazz.getMethods()). + filter(m -> m.getAnnotation(Test.class) != null). + sorted(Comparator.comparing(Method::getName)). + forEach(m -> { + try { + m.invoke(instance); + } catch (IllegalAccessException | InvocationTargetException e) { + throw new RuntimeException(e); + } + }); + } catch (ReflectiveOperationException e) { + throw new AssertionError(e); + } + } + + public static class BSM { + Handle handle; + Method method; + public BSM(Handle handle, Method method) { + this.handle = handle; + this.method = method; + } + } +} diff --git a/src/test/indy/perf/IndyPromotionTest.java b/src/test/indy/perf/IndyPromotionTest.java new file mode 100644 index 0000000000..6dda492749 --- /dev/null +++ b/src/test/indy/perf/IndyPromotionTest.java @@ -0,0 +1,95 @@ +package indy.perf; + +import org.junit.FixMethodOrder; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TestRule; +import org.junit.rules.TestWatcher; +import org.junit.runner.Description; +import org.junit.runners.MethodSorters; +import org.objectweb.asm.Opcodes; + +import java.io.IOException; +import java.lang.invoke.*; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.Comparator; + +import static indy.perf.IndyPerfUtil.*; + +@FixMethodOrder( MethodSorters.NAME_ASCENDING ) +public class IndyPromotionTest { + @Rule + public final TestRule watchman = new TestWatcher() { + @Override + protected void starting(Description description) { + System.out.println(description + "\n\n\tstarted."); + } + + @Override + protected void finished(Description description) { + System.out.println(description + "\tdone."); + } + }; + + public static int foo(int i) { + return i; + } + + private static MethodHandle INVOKER = getHandle(IndyPromotionTest.class, "invoker"); + private static MethodHandle BS2 = getHandle(IndyPromotionTest.class, "bootstrapStage2"); + + @SuppressWarnings("unused") + public static CallSite bootstrap(MethodHandles.Lookup caller, String name, MethodType type) { + MutableCallSite callsite = new MutableCallSite(type); + callsite.setTarget(INVOKER.bindTo(callsite)); + return callsite; + } + + @SuppressWarnings("unused") + public static int invoker(MutableCallSite callsite, int i) { + try { + Method m = IndyPromotionTest.class.getMethod("foo", int.class); + callsite.setTarget(BS2.bindTo(callsite)); + return (int) m.invoke(null, i); + } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { + throw new RuntimeException(e); + } + } + + @SuppressWarnings("unused") + public static int bootstrapStage2(MutableCallSite callsite, int i) { + MethodHandle handle; + try { + handle = LOOKUP.findStatic(IndyDirectTest.class, "foo", callsite.type()); + callsite.setTarget(handle); + return (int) handle.invokeExact(i); + } catch (Throwable e) { + throw new RuntimeException(e); + } + } + + @Test + public void t1_indyPromotion() throws ReflectiveOperationException, IOException { + execute(null); + } + + @Test + public void t2_indyPromotion() throws ReflectiveOperationException, IOException { + execute("t2_indyPromotion"); + } + + public void execute(String name) throws ReflectiveOperationException, IOException { + Class<? extends Runnable> c = writeIndyCall(getBSM(IndyPromotionTest.class).handle, "foo", "(I)I", (mv -> { + mv.visitInsn(Opcodes.ICONST_1); + })); + Runnable r = c.getDeclaredConstructor().newInstance(); + perf(r, name); + } + + public static void main(String[] args) { + executeTests(IndyPromotionTest.class); + } + +} diff --git a/src/test/indy/perf/RuntimeCallsiteGenTest.java b/src/test/indy/perf/RuntimeCallsiteGenTest.java new file mode 100644 index 0000000000..008401dec2 --- /dev/null +++ b/src/test/indy/perf/RuntimeCallsiteGenTest.java @@ -0,0 +1,93 @@ +package indy.perf; + +import org.junit.FixMethodOrder; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TestRule; +import org.junit.rules.TestWatcher; +import org.junit.runner.Description; +import org.junit.runners.MethodSorters; + +import java.io.IOException; +import java.lang.invoke.*; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.Comparator; + +import static indy.perf.IndyPerfUtil.*; + +@FixMethodOrder( MethodSorters.NAME_ASCENDING ) +public class RuntimeCallsiteGenTest { + @Rule + public final TestRule watchman = new TestWatcher() { + @Override + protected void starting(Description description) { + System.out.println(description + "\n\n\tstarted."); + } + + @Override + protected void finished(Description description) { + System.out.println(description + "\tdone."); + } + }; + + public static int foo(int i) { + return i; + } + + @Test + public void t1_callSiteGen() throws ReflectiveOperationException, IOException { + execute(null); + } + + @Test + public void t2_callSiteGen() throws ReflectiveOperationException, IOException { + execute("t2_runtimeCallSiteGen"); + } + + public static class DefaultCallSite implements CallSiteGen { + private CallSiteGen[] array; + private int index; + public DefaultCallSite(CallSiteGen[] array, int index) { + this.array = array; + this.index = index; + array[index] = this; + } + @Override + public Object call(Object[] args) { + Class<CallSiteGen> clazz = IndyPerfUtil.writeCallSiteImpl(CallSiteGen.class, RuntimeCallsiteGenTest.class); + CallSiteGen target = null; + try { + target = clazz.getDeclaredConstructor().newInstance(); + } catch (InstantiationException | IllegalAccessException | InvocationTargetException | + NoSuchMethodException e) { + throw new AssertionError(e); + } + array[index] = target; + return target.call(args); + } + } + + public interface CallSiteGen { + Object call(Object[] args); + } + + public CallSiteGen[] CSA = new CallSiteGen[1]; + + public void execute(String name) throws ReflectiveOperationException, IOException { + CSA[0] = new DefaultCallSite(CSA, 0); + Runnable r = new Runnable() { + @Override + public void run() { + CSA[0].call(new Object[]{1}); + } + }; + perf(r, name); + } + + public static void main(String[] args) { + executeTests(RuntimeCallsiteGenTest.class); + } + +} diff --git a/src/test/indy/perf/SimpleReflectiveTest.java b/src/test/indy/perf/SimpleReflectiveTest.java new file mode 100644 index 0000000000..6843e26dc5 --- /dev/null +++ b/src/test/indy/perf/SimpleReflectiveTest.java @@ -0,0 +1,94 @@ +package indy.perf; + +import org.junit.FixMethodOrder; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TestRule; +import org.junit.rules.TestWatcher; +import org.junit.runner.Description; +import org.junit.runners.MethodSorters; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.Comparator; + +import static indy.perf.IndyPerfUtil.executeTests; +import static indy.perf.IndyPerfUtil.perf; + +@FixMethodOrder( MethodSorters.NAME_ASCENDING ) +public class SimpleReflectiveTest { + @Rule + public final TestRule watchman = new TestWatcher() { + @Override + protected void starting(Description description) { + System.out.println(description + "\n\n\tstarted."); + } + + @Override + protected void finished(Description description) { + System.out.println(description + "\tdone."); + } + }; + + public static int foo(int i) { + return i; + } + + @Test + public void reflectiveCall_t1() { + execute_reflectiveCall(null); + } + + @Test + public void reflectiveCall_t2() { + execute_reflectiveCall("reflectiveCall"); + } + + public void execute_reflectiveCall(String name) { + Runnable r = new Runnable() { + @Override + public void run() { + try { + Method m = SimpleReflectiveTest.class.getMethod("foo", int.class); + m.invoke(null, 1); + } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { + throw new RuntimeException(e); + } + } + }; + perf(r,name); + } + + @Test + public void reflectiveCallCached_t1() { + execute_reflectiveCallCached(null); + } + + @Test + public void reflectiveCallCached_t2() { + execute_reflectiveCallCached("reflectiveCallCached"); + } + + public void execute_reflectiveCallCached(String name) { + Runnable r = new Runnable() { + Method m = null; + @Override + public void run() { + try { + if (m == null) { + m = SimpleReflectiveTest.class.getMethod("foo", int.class); + } + m.invoke(null, 1); + } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { + throw new RuntimeException(e); + } + } + }; + perf(r, name); + } + + public static void main(String[] args) { + executeTests(SimpleReflectiveTest.class); + } +} diff --git a/src/test/indy/perf/StaticCallsiteGenTest.java b/src/test/indy/perf/StaticCallsiteGenTest.java new file mode 100644 index 0000000000..243f154708 --- /dev/null +++ b/src/test/indy/perf/StaticCallsiteGenTest.java @@ -0,0 +1,91 @@ +package indy.perf; + +import org.junit.FixMethodOrder; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TestRule; +import org.junit.rules.TestWatcher; +import org.junit.runner.Description; +import org.junit.runners.MethodSorters; + +import java.io.IOException; +import java.lang.invoke.*; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.Comparator; + +import static indy.perf.IndyPerfUtil.executeTests; +import static indy.perf.IndyPerfUtil.perf; + +@FixMethodOrder( MethodSorters.NAME_ASCENDING ) +public class StaticCallsiteGenTest { + @Rule + public final TestRule watchman = new TestWatcher() { + @Override + protected void starting(Description description) { + System.out.println(description + "\n\n\tstarted."); + } + + @Override + protected void finished(Description description) { + System.out.println(description + "\tdone."); + } + }; + + public static int foo(int i) { + return i; + } + + @Test + public void t1_staticCallSiteGen() throws ReflectiveOperationException, IOException { + execute(null); + } + + @Test + public void t2_staticCallSiteGen() throws ReflectiveOperationException, IOException { + execute("t2_statiCallSiteGen"); + } + + public static class DefaultCallSite implements CallSiteGen { + private CallSiteGen[] array; + private int index; + public DefaultCallSite(CallSiteGen[] array, int index) { + this.array = array; + this.index = index; + array[index] = this; + } + @Override + public Object call(Object[] args) { + CallSiteGen target = new CallSiteGen() { + @Override + public Object call(Object[] args) { + return StaticCallsiteGenTest.foo((Integer) args[0]); + } + }; + array[index] = target; + return target.call(args); + } + } + + public interface CallSiteGen { + Object call(Object[] args); + } + + public CallSiteGen[] CSA = new CallSiteGen[1]; + + public void execute(String name) throws ReflectiveOperationException, IOException { + CSA[0] = new DefaultCallSite(CSA, 0); + Runnable r = new Runnable() { + @Override + public void run() { + CSA[0].call(new Object[]{1}); + } + }; + perf(r, name); + } + + public static void main(String[] args) { + executeTests(StaticCallsiteGenTest.class); + } +}
