MethodHandle性能

DoS*_*ver 18 java performance methodhandle

我写了一个小的基准测试性能java.lang.invoke.MethodHandle,java.lang.reflect.Method和方法的直接调用.

我读到的MethodHandle.invoke()表现几乎与直接通话相同.但我的测试结果显示另一个:MethodHandle调用比反射慢大约三倍.我的问题是什么?可能这是一些JIT优化的结果?

public class Main {
    public static final int COUNT = 100000000;
    static TestInstance test = new TestInstance();

    static void testInvokeDynamic() throws NoSuchMethodException, IllegalAccessException {
        int [] ar = new int[COUNT];

        MethodHandles.Lookup lookup = MethodHandles.lookup();
        MethodType mt = MethodType.methodType(int.class);

        MethodHandle handle = lookup.findStatic(TestInstance.class, "publicStaticMethod", mt) ;

        try {
            long start = System.currentTimeMillis();

            for (int i=0; i<COUNT; i++) {
                ar[i] = (int)handle.invokeExact();
            }

            long stop = System.currentTimeMillis();

            System.out.println(ar);

            System.out.println("InvokeDynamic time: " + (stop - start));
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
    }

    static void testDirect() {
        int [] ar = new int[COUNT];

        try {
            long start = System.currentTimeMillis();

            for (int i=0; i<COUNT; i++) {
                ar[i] = TestInstance.publicStaticMethod();
            }

            long stop = System.currentTimeMillis();

            System.out.println(ar);

            System.out.println("Direct call time: " + (stop - start));
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
    }

    static void testReflection() throws NoSuchMethodException {
        int [] ar = new int[COUNT];

        Method method = test.getClass().getMethod("publicStaticMethod");

        try {
            long start = System.currentTimeMillis();

            for (int i=0; i<COUNT; i++) {
                ar[i] = (int)method.invoke(test);
            }

            long stop = System.currentTimeMillis();

            System.out.println(ar);

            System.out.println("Reflection time: " + (stop - start));
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
    }

    static void testReflectionAccessible() throws NoSuchMethodException {
        int [] ar = new int[COUNT];

        Method method = test.getClass().getMethod("publicStaticMethod");
        method.setAccessible(true);

        try {
            long start = System.currentTimeMillis();

            for (int i=0; i<COUNT; i++) {
                ar[i] = (int)method.invoke(test);
            }

            long stop = System.currentTimeMillis();

            System.out.println(ar);

            System.out.println("Reflection accessible time: " + (stop - start));
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
    }

    public static void main(String ... args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException, InterruptedException {
        Thread.sleep(5000);

        Main.testDirect();
        Main.testInvokeDynamic();
        Main.testReflection();
        Main.testReflectionAccessible();

        System.out.println("\n___\n");

        System.gc();
        System.gc();

        Main.testDirect();
        Main.testInvokeDynamic();
        Main.testReflection();
        Main.testReflectionAccessible();
    }
}
Run Code Online (Sandbox Code Playgroud)

环境: java版"1.7.0_11"Java(TM)SE运行时环境(版本1.7.0_11-b21)Java HotSpot(TM)64位服务器VM(版本23.6-b04,混合模式)操作系统 - Windows 7 64

Nig*_*ler 5

看起来@AlekseyShipilev 参考不同的查询间接回答了这个问题。在以下链接中 如何提高 Field.set 的性能(可能使用 MethodHandles)?

如果您仔细阅读,您将看到显示类似发现的其他基准。直接调用很可能可以简单地通过 JIT 进行优化,根据上面的发现,差异是:

  • MethodHandle.invoke =~195ns
  • MethodHandle.invokeExact =~10ns
  • 直接调用 = 1.266ns

所以 - 直接调用仍然会更快,但是 MH 非常快。对于大多数用例来说,这应该足够了,并且肯定比旧的反射框架更快(顺便说一句 - 根据上面的发现,反射在 java8 vm 下也明显更快)

如果这种差异在您的系统中很重要,我建议您找到不同的模式而不是直接反射来支持直接调用。


men*_*ics 1

看来其他人也看到了类似的结果: http://vanillajava.blogspot.com/2011/08/methodhandle-performance-in-java-7.html

这是别人的: http://andrewtill.blogspot.com/2011/08/using-method-handles.html

我运行了第二个,发现即使修复测试以进行热身,它们的速度也大致相同。不过,我修复了它,这样它就不会每次都创建 args 数组。在默认计数下,它会产生相同的结果:方法句柄更快一些。但我计数了 10000000(默认*10)并且反射速度更快。

因此,我建议使用参数进行测试。我想知道 MethodHandles 是否能更有效地处理参数?另外,检查更改计数——迭代次数。

@meriton 对这个问题的评论链接到他的工作,看起来非常有帮助:通过反射调用 Java 中的 getter:重复调用它的最快方法是什么(性能和可扩展性方面)?