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);
+    }
+}

Reply via email to