MethodHandle性能

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

我读到MethodHandle.invoke()性能几乎与直接调用相同。 但我的测试结果显示另一个: MethodHandle调用比reflection慢大约三倍。 我的问题是什么? 可能这是一些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(); } } 

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

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

如果您通读,您将看到显示类似发现的其他基准。 JIT可能只是通过以下方式简单地优化直接调用。根据上述发现,差异是:MethodHandle.invoke = ~195ns MethodHandle.invokeExact = ~10ns直接调用= 1.266ns

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

如果这个差异在你的系统中很重要,我会建议找到不同的模式,而不是直接reflection,这将支持直接调用。

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

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

我跑了第二个,看到他们的速度大致相同,甚至还要确保测试进行预热。 但是,我修复了它,所以每次都没有创建一个args数组。 在默认计数下,它产生了相同的结果:methodhandles更快一些。 但我的计数为10000000(默认值为* 10),reflection速度更快。

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

@meriton对这个问题的评论链接到他的工作并且看起来非常有用: 通过reflection调用Java中的getter:重复调用它的最快方法是什么(性能和可伸缩性明智)?

如果publicStaticMethod是一个简单的实现,比如返回一个常量,那么直接调用很可能是由JIT编译器内联的。 methodHandles可能无法实现这一点。

RE http://vanillajava.blogspot.com/2011/08/methodhandle-performance-in-java-7.html示例,如上所述,评论其不是很好的实现。 如果在计算循环中将类型转换更改为int(而不是Integer),则结果更接近直接方法调用。

随着(创建和调用返回随机int的未来任务)的复杂实现给予基准更接近的数字,其中MethodStatic比直接方法最多约10%。 因此,由于JIT优化,您可能会看到性能降低3倍