我编写了一个测试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();
}
}环境: java版本"1.7.0_11“Java(TM) SE运行时环境(build 1.7.0_11-b21) Java HotSpot(TM) 64位服务器VM (build 23.6-b04,混合模式) OS - Windows 7 64
发布于 2015-01-26 18:31:19
看起来,@AlekseyShipilev在引用另一个查询时间接回答了这个问题。在下面的链接How can I improve performance of Field.set (perhap using MethodHandles)?中
如果你仔细阅读,你会看到更多的基准,显示类似的结果。很可能,直接调用可以简单地通过JIT优化,根据上面的发现,不同之处在于:
所以直拨电话仍然会更快,但是MH是非常快的.对于大多数用例来说,这应该足够了,并且肯定比旧的反射框架(顺便说一下,根据上面的发现,在java8 vm下反射也要快得多)更快。
如果这种差异在您的系统中是显著的,我建议找出不同的模式,而不是直接反射,这将支持直接调用。
发布于 2013-04-10 18:53:23
似乎其他人也看到了类似的结果: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关于这个问题的评论链接到他的工作,看起来很有帮助:Calling a getter in Java though reflection: What's the fastest way to repeatedly call it (performance and scalability wise)?
发布于 2014-02-04 17:13:17
如果publicStaticMethod是一个简单的实现,比如返回一个常量,那么直接调用很有可能是由JIT编译器嵌入的。这在methodHandles中可能是不可能的。
RE http://vanillajava.blogspot.com/2011/08/methodhandle-performance-in-java-7.html示例,正如前面提到的,它的实现不是很好。如果将计算循环中的类型转换更改为int (而不是Integer),则结果更接近于直接方法调用。
在复杂的实现(创建和调用一个返回随机int的未来任务)的情况下,给出了MethodStatic比直接方法慢10%的更接近的基准数。因此,由于JIT优化,您可能会看到性能下降了3倍。
https://stackoverflow.com/questions/15621434
复制相似问题