方法处理性能

2022-09-01 21:26:40

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

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

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

环境: java 版本 “1.7.0_11” Java(TM) SE Runtime Environment (build 1.7.0_11-b21) Java HotSpot(TM) 64-Bit Server VM (build 23.6-b04, mixed mode) OS - Windows 7 64


答案 1

看起来这是由@AlekseyShipilev间接回答的,指的是不同的查询。在下面的链接中,如何提高Field.set的性能(可能使用MethodHandles)?

如果您通读,您将看到显示类似发现的其他基准测试。直接调用很可能可以通过JIT简单地进行优化,根据上面的发现,区别在于:

  • MethodHandle.invoke =~195ns
  • MethodHandle.invokeExact =~10ns
  • 直接呼叫 = 1.266ns

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

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


答案 2

似乎其他人也看到了类似的结果: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:重复调用它的最快方法是什么(性能和可扩展性明智)?


推荐