I am working on a simple project and need to retrieve a bean property. First I use reflection. Then I did some investigation on invokedynamic and Method Handler for better performance.
Although invokeExact
is much faster than reflection, invoke is much slower than reflection.
Test Env:
The tp/ms I got is about this:
mhInvoke * 5 = reflect
reflect * 6 = mhInvokeExact
mhInvokeExact * 10 = direct call
Here is the performance test output(I run it twice):
Ref tpms = 10479
mh invoke tpms = 273
mh invoke with convert tpms = 957
mh invoke exact tpms = 78033
invoke directly tpms = 883011
Ref tpms = 14181
mh invoke tpms = 282
mh invoke with convert tpms = 984
mh invoke exact tpms = 88768
invoke directly tpms = 883011
Here is my test code:
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.reflect.Method;
/**
* User: Mark Zang
* Date: 2015/4/28
* Time: 13:00
*/
public class PerfTestAppMain {
String strVar = String.valueOf(System.currentTimeMillis());
public String getStrVar() {
return strVar;
}
static int count = 1024 * 1024 * 16;
public static void main(String[] args) throws Throwable {
ref();
mhInvoke();
mhInvokeConvert();
mhInvokeExact();
invoke();
System.out.println();
System.out.println();
ref();
mhInvoke();
mhInvokeConvert();
mhInvokeExact();
invoke();
}
static void ref() throws Throwable {
PerfTestAppMain bean = new PerfTestAppMain();
Method ref = PerfTestAppMain.class.getMethod("getStrVar");
Object ret = null;
long start = System.currentTimeMillis();
for (int i = 0; i < count; i++) {
ret = ref.invoke(bean);
}
long end = System.currentTimeMillis();
System.out.println("Ref tpms = " + ((count) / (end - start)));
}
static void mhInvoke() throws Throwable {
PerfTestAppMain bean = new PerfTestAppMain();
MethodHandle mh = MethodHandles.lookup().findVirtual(
PerfTestAppMain.class,
"getStrVar",
MethodType.methodType(String.class))
.bindTo(bean);
Object ret = null;
long start = System.currentTimeMillis();
for (int i = 0; i < count; i++) {
ret = mh.invoke();
}
long end = System.currentTimeMillis();
System.out.println("mh invoke tpms = " + ((count) / (end - start)));
}
static void mhInvokeConvert() throws Throwable {
PerfTestAppMain bean = new PerfTestAppMain();
MethodHandle mh = MethodHandles.lookup().findVirtual(
PerfTestAppMain.class,
"getStrVar",
MethodType.methodType(String.class))
.bindTo(bean);
String ret = null;
long start = System.currentTimeMillis();
for (int i = 0; i < count; i++) {
ret = (String) mh.invoke();
}
long end = System.currentTimeMillis();
System.out.println("mh invoke with convert tpms = " + ((count) / (end - start)));
}
static void mhInvokeExact() throws Throwable {
PerfTestAppMain bean = new PerfTestAppMain();
MethodHandle mh = MethodHandles.lookup().findVirtual(
PerfTestAppMain.class,
"getStrVar",
MethodType.methodType(String.class));
String ret = null;
long start = System.currentTimeMillis();
for (int i = 0; i < count; i++) {
ret = (String) mh.invokeExact(bean);
}
long end = System.currentTimeMillis();
System.out.println("mh invoke exact tpms = " + ((count) / (end - start)));
}
static void invoke() throws Throwable {
PerfTestAppMain bean = new PerfTestAppMain();
long start = System.currentTimeMillis();
for (int i = 0; i < count; i++) {
bean.getStrVar();
}
long end = System.currentTimeMillis();
System.out.println("invoke directly tpms = " + ((count) / (end - start + 1)));
}
}
invokeExact
can't meet my use case because I don't know the exact return type at compile time. It seems that the return type (force cast) is a critical key for MethodHandle
's performance.
This does not seem to be an expected result because the MethodType
has the exact return type. Why it is still important to do a force cast to enhance the performance?
Is there some documentation that explains the details about this? Also, are there any docs about comparing the impl details of using reflection vs method handlers?
Your benchmark is flawed for many reasons. You need to write a harnessed benchmark instead to create a controlled environment where the JIT compiler does not optimize away the code you are measuring. I have once written such a benchmark targeting method handles compared to reflection: https://gist.github.com/raphw/881e1745996f9d314ab0
Invokeexact does not cast or convert types. Instead, it adapts the actual types of the arguments and return value. This can be benifitial when primitive argument or return types are known at compile time as the Java compiler creates a sythetic signature for the method call. This avoids boxing which is enforced by the reflection API. Other than that, method handles do not offer performance benefits over the reflection API. I have recently blogged on this matter: http://mydailyjava.blogspot.fr/2015/03/dismantling-invokedynamic.html?m=1