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
看起来@AlekseyShipilev 参考不同的查询间接回答了这个问题。在以下链接中 如何提高 Field.set 的性能(可能使用 MethodHandles)?
如果您仔细阅读,您将看到显示类似发现的其他基准。直接调用很可能可以简单地通过 JIT 进行优化,根据上面的发现,差异是:
所以 - 直接调用仍然会更快,但是 MH 非常快。对于大多数用例来说,这应该足够了,并且肯定比旧的反射框架更快(顺便说一句 - 根据上面的发现,反射在 java8 vm 下也明显更快)
如果这种差异在您的系统中很重要,我建议您找到不同的模式而不是直接反射来支持直接调用。
看来其他人也看到了类似的结果: 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:重复调用它的最快方法是什么(性能和可扩展性方面)?