Skip to content

Instantly share code, notes, and snippets.

@raphw
Last active March 4, 2024 00:14
Show Gist options
  • Star 26 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save raphw/881e1745996f9d314ab0 to your computer and use it in GitHub Desktop.
Save raphw/881e1745996f9d314ab0 to your computer and use it in GitHub Desktop.
Java MethodHandle and reflection benchmark
package benchmark;
import org.openjdk.jmh.annotations.*;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.concurrent.TimeUnit;
@State(Scope.Thread)
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
public class FieldBenchmark {
private String value = "foo";
private int primitiveValue = 42;
enum Access {
INSTANCE;
private String value = "bar";
}
private Field reflective, reflectiveAccessible, reflectivePrimitive, reflectiveAccessiblePrimitive, reflectiveAccessiblePrivate;
private MethodHandle methodHandle, methodHandleUnreflected, methodHandlePrimitive, methodHandleUnreflectedPrimitive, methodHandleUnreflectedPrivate;
private static final MethodHandle METHOD_HANDLE_INLINE, METHOD_HANDLE_UNREFLECTED_INLINE, METHOD_HANDLE_PRIMITIVE_INLINE, METHOD_HANDLE_UNREFLECTED_PRIMITIVE, METHOD_HANDLE_UNREFLECTED_PRIVATE;
static {
try {
METHOD_HANDLE_INLINE = MethodHandles.lookup().findGetter(FieldBenchmark.class, "value", String.class);
METHOD_HANDLE_UNREFLECTED_INLINE = MethodHandles.lookup().unreflectGetter(FieldBenchmark.class.getDeclaredField("value"));
METHOD_HANDLE_PRIMITIVE_INLINE = MethodHandles.lookup().findGetter(FieldBenchmark.class, "primitiveValue", int.class);
METHOD_HANDLE_UNREFLECTED_PRIMITIVE = MethodHandles.lookup().unreflectGetter(FieldBenchmark.class.getDeclaredField("primitiveValue"));
Field reflectiveAccessiblePrivate = Access.class.getDeclaredField("value");
reflectiveAccessiblePrivate.setAccessible(true);
METHOD_HANDLE_UNREFLECTED_PRIVATE = MethodHandles.lookup().unreflectGetter(reflectiveAccessiblePrivate);
} catch (Exception e) {
throw new AssertionError();
}
}
@Setup
public void setup() throws Exception {
reflective = FieldBenchmark.class.getDeclaredField("value");
reflectiveAccessible = FieldBenchmark.class.getDeclaredField("value");
reflectiveAccessible.setAccessible(true);
reflectivePrimitive = FieldBenchmark.class.getDeclaredField("primitiveValue");
reflectiveAccessiblePrimitive = FieldBenchmark.class.getDeclaredField("primitiveValue");
reflectiveAccessiblePrimitive.setAccessible(true);
methodHandle = MethodHandles.lookup().findGetter(FieldBenchmark.class, "value", String.class);
methodHandleUnreflected = MethodHandles.lookup().unreflectGetter(reflective);
methodHandlePrimitive = MethodHandles.lookup().findGetter(FieldBenchmark.class, "primitiveValue", int.class);
methodHandleUnreflectedPrimitive = MethodHandles.lookup().unreflectGetter(reflectivePrimitive);
reflectiveAccessiblePrivate = Access.class.getDeclaredField("value");
reflectiveAccessiblePrivate.setAccessible(true);
methodHandleUnreflectedPrivate = MethodHandles.lookup().unreflectGetter(reflectiveAccessiblePrivate);
}
@Benchmark
public Object normal() {
return value;
}
@Benchmark
public Object reflection() throws InvocationTargetException, IllegalAccessException {
return reflective.get(this);
}
@Benchmark
public Object reflectionAccessible() throws InvocationTargetException, IllegalAccessException {
return reflectiveAccessible.get(this);
}
@Benchmark
public Object handle() throws Throwable {
return methodHandle.invoke(this);
}
@Benchmark
public Object handleExact() throws Throwable {
return (String) methodHandle.invokeExact(this);
}
@Benchmark
public Object handleUnreflected() throws Throwable {
return methodHandleUnreflected.invoke(this);
}
@Benchmark
public Object handleUnreflectedExact() throws Throwable {
return (String) methodHandleUnreflected.invokeExact(this);
}
@Benchmark
public int primitive() {
return primitiveValue;
}
@Benchmark
public int reflectionPrimitive() throws InvocationTargetException, IllegalAccessException {
return (int) reflectivePrimitive.get(this);
}
@Benchmark
public int reflectionAccessiblePrimitive() throws InvocationTargetException, IllegalAccessException {
return (int) reflectiveAccessiblePrimitive.get(this);
}
@Benchmark
public int reflectionSpecializedPrimitive() throws InvocationTargetException, IllegalAccessException {
return reflectivePrimitive.getInt(this);
}
@Benchmark
public int reflectionAccessibleSpecializedPrimitive() throws InvocationTargetException, IllegalAccessException {
return reflectiveAccessiblePrimitive.getInt(this);
}
@Benchmark
public int handlePrimitive() throws Throwable {
return (int) methodHandlePrimitive.invoke(this);
}
@Benchmark
public int handleExactPrimitive() throws Throwable {
return (int) methodHandlePrimitive.invokeExact(this);
}
@Benchmark
public int handleUnreflectedPrimitive() throws Throwable {
return (int) methodHandleUnreflectedPrimitive.invoke(this);
}
@Benchmark
public int handleUnreflectedExactPrimitive() throws Throwable {
return (int) methodHandleUnreflectedPrimitive.invokeExact(this);
}
@Benchmark
public String privateNormal() {
return Access.INSTANCE.value; // accessor method
}
@Benchmark
public Object reflectionAccessiblePrivate() throws Exception {
return reflectiveAccessiblePrivate.get(Access.INSTANCE);
}
@Benchmark
public String handleUnreflectedPrivate() throws Throwable {
return (String) methodHandleUnreflectedPrivate.invokeExact((Access) Access.INSTANCE);
}
@Benchmark
public Object handleInline() throws Throwable {
return METHOD_HANDLE_INLINE.invoke(this);
}
@Benchmark
public Object handleExactInline() throws Throwable {
return (String) METHOD_HANDLE_INLINE.invokeExact(this);
}
@Benchmark
public Object handleUnreflectedInline() throws Throwable {
return METHOD_HANDLE_UNREFLECTED_INLINE.invoke(this);
}
@Benchmark
public Object handleUnreflectedExactInline() throws Throwable {
return (String) METHOD_HANDLE_UNREFLECTED_INLINE.invokeExact(this);
}
@Benchmark
public int handlePrimitiveInline() throws Throwable {
return (int) METHOD_HANDLE_PRIMITIVE_INLINE.invoke(this);
}
@Benchmark
public int handleExactPrimitiveInline() throws Throwable {
return (int) METHOD_HANDLE_PRIMITIVE_INLINE.invokeExact(this);
}
@Benchmark
public int handleUnreflectedPrimitiveInline() throws Throwable {
return (int) METHOD_HANDLE_UNREFLECTED_PRIMITIVE.invoke(this);
}
@Benchmark
public int handleUnreflectedExactPrimitiveInline() throws Throwable {
return (int) METHOD_HANDLE_UNREFLECTED_PRIMITIVE.invokeExact(this);
}
@Benchmark
public String handleUnreflectedPrivateInline() throws Throwable {
return (String) METHOD_HANDLE_UNREFLECTED_PRIVATE.invokeExact((Access) Access.INSTANCE);
}
}
package benchmark;
import org.openjdk.jmh.annotations.*;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.reflect.Method;
import java.util.concurrent.TimeUnit;
@State(Scope.Thread)
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
public class InvocationBenchmark {
private String s1 = "foo", s2 = "bar", s3 = "qux", s4 = "baz";
private String method(String a, String b, String c, String d) {
return a + b + c + d;
}
private int i1 = 1, i2 = 2, i3 = 3, i4 = 4;
private int methodPrimitive(int a, int b, int c, int d) {
return a + b + c + d;
}
enum Access {
INSTANCE;
private String method(String a, String b, String c, String d) {
return a + b + c + d;
}
}
private Method method, methodAccessible, methodPrimitive, methodAccessiblePrimitive, methodAccessiblePrivate;
private MethodHandle methodHandle, methodHandleUnreflected, methodHandlePrimitive, methodHandleUnreflectedPrimitive, methodHandleUnreflectedPrivate;
private static final MethodHandle METHOD_HANDLE_INLINE, METHOD_HANDLE_UNREFLECTED_INLINE, METHOD_HANDLE_PRIMITIVE_INLINE, METHOD_HANDLE_UNREFLECTED_PRIMITIVE_INLINE, METHOD_HANDLE_UNREFLECTED_PRIVATE_INLINE;
static {
try {
Method methodAccessible = InvocationBenchmark.class.getDeclaredMethod("method", String.class, String.class, String.class, String.class);
methodAccessible.setAccessible(true);
METHOD_HANDLE_INLINE = MethodHandles.lookup().findVirtual(InvocationBenchmark.class, "method",
MethodType.methodType(String.class, String.class, String.class, String.class, String.class));
METHOD_HANDLE_UNREFLECTED_INLINE = MethodHandles.lookup().unreflect(methodAccessible);
Method methodAccessiblePrimitive = InvocationBenchmark.class.getDeclaredMethod("methodPrimitive", int.class, int.class, int.class, int.class);
methodAccessiblePrimitive.setAccessible(true);
METHOD_HANDLE_PRIMITIVE_INLINE = MethodHandles.lookup().findVirtual(InvocationBenchmark.class, "methodPrimitive",
MethodType.methodType(int.class, int.class, int.class, int.class, int.class));
METHOD_HANDLE_UNREFLECTED_PRIMITIVE_INLINE = MethodHandles.lookup().unreflect(methodAccessiblePrimitive);
Method methodAccessiblePrivate = Access.class.getDeclaredMethod("method", String.class, String.class, String.class, String.class);
methodAccessiblePrivate.setAccessible(true);
METHOD_HANDLE_UNREFLECTED_PRIVATE_INLINE = MethodHandles.lookup().unreflect(methodAccessiblePrivate);
} catch (Exception e) {
throw new AssertionError();
}
}
@Setup
public void setUp() throws Exception {
method = InvocationBenchmark.class.getDeclaredMethod("method", String.class, String.class, String.class, String.class);
methodAccessible = InvocationBenchmark.class.getDeclaredMethod("method", String.class, String.class, String.class, String.class);
methodAccessible.setAccessible(true);
methodHandle = MethodHandles.lookup().findVirtual(InvocationBenchmark.class, "method",
MethodType.methodType(String.class, String.class, String.class, String.class, String.class));
methodHandleUnreflected = MethodHandles.lookup().unreflect(methodAccessible);
methodPrimitive = InvocationBenchmark.class.getDeclaredMethod("methodPrimitive", int.class, int.class, int.class, int.class);
methodAccessiblePrimitive = InvocationBenchmark.class.getDeclaredMethod("methodPrimitive", int.class, int.class, int.class, int.class);
methodAccessiblePrimitive.setAccessible(true);
methodHandlePrimitive = MethodHandles.lookup().findVirtual(InvocationBenchmark.class, "methodPrimitive",
MethodType.methodType(int.class, int.class, int.class, int.class, int.class));
methodHandleUnreflectedPrimitive = MethodHandles.lookup().unreflect(methodAccessiblePrimitive);
methodAccessiblePrivate = Access.class.getDeclaredMethod("method", String.class, String.class, String.class, String.class);
methodAccessiblePrivate.setAccessible(true);
methodHandleUnreflectedPrivate = MethodHandles.lookup().unreflect(methodAccessiblePrivate);
}
@Benchmark
public Object normal() throws Exception {
return method(s1, s2, s3, s4);
}
@Benchmark
public Object reflection() throws Exception {
return method.invoke(this, s1, s2, s3, s4);
}
@Benchmark
public Object reflectionAccessible() throws Exception {
return methodAccessible.invoke(this, s1, s2, s3, s4);
}
@Benchmark
public Object handle() throws Throwable {
return methodHandle.invoke(this, s1, s2, s3, s4);
}
@Benchmark
public Object handleExact() throws Throwable {
return (String) methodHandle.invokeExact(this, s1, s2, s3, s4);
}
@Benchmark
public Object handleUnreflectedExact() throws Throwable {
return (String) methodHandleUnreflected.invokeExact(this, s1, s2, s3, s4);
}
@Benchmark
public int primitive() {
return methodPrimitive(i1, i2, i3, i4);
}
@Benchmark
public int reflectionPrimitive() throws Throwable {
return (int) methodPrimitive.invoke(this, i1, i2, i3, i4);
}
@Benchmark
public int reflectionAccessiblePrimitive() throws Throwable {
return (int) methodAccessiblePrimitive.invoke(this, i1, i2, i3, i4);
}
@Benchmark
public int handlePrimitive() throws Throwable {
return (int) methodHandlePrimitive.invoke(this, i1, i2, i3, i4);
}
@Benchmark
public int handlePrimitiveBoxed() throws Throwable {
return (Integer) methodHandlePrimitive.invoke(this, Integer.valueOf(i1), Integer.valueOf(i2), Integer.valueOf(i3), Integer.valueOf(i4));
}
@Benchmark
public int handlePrimitiveExact() throws Throwable {
return (int) methodHandlePrimitive.invokeExact(this, i1, i2, i3, i4);
}
@Benchmark
public Object handleUnreflectedPrimitiveExact() throws Throwable {
return (int) methodHandleUnreflectedPrimitive.invokeExact(this, i1, i2, i3, i4);
}
@Benchmark
public Object privateNormal() throws Exception {
return Access.INSTANCE.method(s1, s2, s3, s4); // accessor method indirection
}
@Benchmark
public Object reflectionAccessiblePrivate() throws Exception {
return methodAccessiblePrivate.invoke(Access.INSTANCE, s1, s2, s3, s4);
}
@Benchmark
public Object handleUnreflectedExactPrivate() throws Throwable {
return (String) methodHandleUnreflectedPrivate.invokeExact(Access.INSTANCE, s1, s2, s3, s4);
}
@Benchmark
public Object handleInline() throws Throwable {
return METHOD_HANDLE_INLINE.invoke(this, s1, s2, s3, s4);
}
@Benchmark
public Object handleExactInline() throws Throwable {
return (String) METHOD_HANDLE_INLINE.invokeExact(this, s1, s2, s3, s4);
}
@Benchmark
public Object handleUnreflectedExactInline() throws Throwable {
return (String) METHOD_HANDLE_UNREFLECTED_INLINE.invokeExact(this, s1, s2, s3, s4);
}
@Benchmark
public int handlePrimitiveInline() throws Throwable {
return (int) METHOD_HANDLE_PRIMITIVE_INLINE.invoke(this, i1, i2, i3, i4);
}
@Benchmark
public int handlePrimitiveBoxedInline() throws Throwable {
return (Integer) METHOD_HANDLE_PRIMITIVE_INLINE.invoke(this, Integer.valueOf(i1), Integer.valueOf(i2), Integer.valueOf(i3), Integer.valueOf(i4));
}
@Benchmark
public int handlePrimitiveExactInline() throws Throwable {
return (int) METHOD_HANDLE_UNREFLECTED_PRIMITIVE_INLINE.invokeExact(this, i1, i2, i3, i4);
}
@Benchmark
public int handleUnreflectedPrimitiveExactInline() throws Throwable {
return (int) METHOD_HANDLE_UNREFLECTED_PRIMITIVE_INLINE.invokeExact(this, i1, i2, i3, i4);
}
@Benchmark
public Object handleUnreflectedExactPrivateInline() throws Throwable {
return (String) METHOD_HANDLE_UNREFLECTED_PRIVATE_INLINE.invokeExact(Access.INSTANCE, s1, s2, s3, s4);
}
}
package benchmark;
import org.openjdk.jmh.annotations.*;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.reflect.Method;
import java.util.concurrent.TimeUnit;
@State(Scope.Thread)
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
public class LookupBenchmark {
private String name = "method";
private MethodHandles.Lookup lookup;
private MethodType methodType;
private Class<?> returnType = void.class, declaringType = LookupBenchmark.class;
void method() {
/* empty */
}
@Setup
public void setUp() throws Exception {
lookup = MethodHandles.lookup();
methodType = MethodType.methodType(void.class);
}
@Benchmark
public Method reflection() throws Exception {
return declaringType.getDeclaredMethod(name);
}
@Benchmark
public MethodHandle handle() throws Exception {
return MethodHandles.lookup().findVirtual(declaringType, name, MethodType.methodType(returnType));
}
@Benchmark
public MethodHandle handlePreLookedUp() throws Exception {
return lookup.findVirtual(declaringType, name, methodType);
}
}
Benchmark Mode Cnt Score Error Units
b.FieldBenchmark.normal avgt 20 2,558 ± 0,031 ns/op
b.FieldBenchmark.primitive avgt 20 2,116 ± 0,092 ns/op
b.FieldBenchmark.privateNormal avgt 20 2,514 ± 0,016 ns/op
b.FieldBenchmark.reflection avgt 20 5,855 ± 0,164 ns/op
b.FieldBenchmark.reflectionAccessible avgt 20 5,515 ± 0,013 ns/op
b.FieldBenchmark.reflectionAccessiblePrimitive avgt 20 5,742 ± 0,033 ns/op
b.FieldBenchmark.reflectionAccessiblePrivate avgt 20 5,484 ± 0,089 ns/op
b.FieldBenchmark.reflectionAccessibleSpecializedPrimitive avgt 20 5,850 ± 0,050 ns/op
b.FieldBenchmark.reflectionPrimitive avgt 20 5,783 ± 0,131 ns/op
b.FieldBenchmark.reflectionSpecializedPrimitive avgt 20 5,765 ± 0,030 ns/op
b.FieldBenchmark.handle avgt 20 6,367 ± 0,167 ns/op
b.FieldBenchmark.handleExact avgt 20 7,154 ± 0,128 ns/op
b.FieldBenchmark.handleInline avgt 20 5,289 ± 0,143 ns/op
b.FieldBenchmark.handleExactInline avgt 20 2,523 ± 0,015 ns/op
b.FieldBenchmark.handlePrimitive avgt 20 5,545 ± 0,061 ns/op
b.FieldBenchmark.handleExactPrimitive avgt 20 5,558 ± 0,043 ns/op
b.FieldBenchmark.handlePrimitiveInline avgt 20 2,043 ± 0,055 ns/op
b.FieldBenchmark.handleExactPrimitiveInline avgt 20 2,023 ± 0,029 ns/op
b.FieldBenchmark.handleUnreflected avgt 20 6,151 ± 0,040 ns/op
b.FieldBenchmark.handleUnreflectedExact avgt 20 7,097 ± 0,025 ns/op
b.FieldBenchmark.handleUnreflectedInline avgt 20 5,314 ± 0,067 ns/op
b.FieldBenchmark.handleUnreflectedExactInline avgt 20 2,513 ± 0,005 ns/op
b.FieldBenchmark.handleUnreflectedPrimitive avgt 20 5,643 ± 0,047 ns/op
b.FieldBenchmark.handleUnreflectedExactPrimitive avgt 20 5,544 ± 0,035 ns/op
b.FieldBenchmark.handleUnreflectedPrimitiveInline avgt 20 2,047 ± 0,026 ns/op
b.FieldBenchmark.handleUnreflectedExactPrimitiveInline avgt 20 2,013 ± 0,015 ns/op
b.FieldBenchmark.handleUnreflectedPrivate avgt 20 7,249 ± 0,053 ns/op
b.FieldBenchmark.handleUnreflectedPrivateInline avgt 20 2,558 ± 0,032 ns/op
# Run on Windows 8.1, x86-64 with Java1.8.0_25
Benchmark Mode Cnt Score Error Units
b.InvocationBenchmark.normal avgt 20 28,364 ± 1,378 ns/op
b.InvocationBenchmark.primitive avgt 20 2,469 ± 0,011 ns/op
b.InvocationBenchmark.privateNormal avgt 20 27,122 ± 0,158 ns/op
b.InvocationBenchmark.reflection avgt 20 31,026 ± 0,213 ns/op
b.InvocationBenchmark.reflectionPrimitive avgt 20 29,537 ± 2,152 ns/op
b.InvocationBenchmark.reflectionAccessible avgt 20 30,420 ± 0,216 ns/op
b.InvocationBenchmark.reflectionAccessiblePrimitive avgt 20 28,582 ± 1,160 ns/op
b.InvocationBenchmark.reflectionAccessiblePrivate avgt 20 33,566 ± 3,674 ns/op
b.InvocationBenchmark.handle avgt 20 32,195 ± 2,448 ns/op
b.InvocationBenchmark.handleExact avgt 20 36,736 ± 0,908 ns/op
b.InvocationBenchmark.handleInline avgt 20 33,549 ± 3,397 ns/op
b.InvocationBenchmark.handleExactInline avgt 20 27,241 ± 0,114 ns/op
b.InvocationBenchmark.handlePrimitive avgt 20 8,525 ± 0,055 ns/op
b.InvocationBenchmark.handlePrimitiveBoxed avgt 20 10,443 ± 0,370 ns/op
b.InvocationBenchmark.handlePrimitiveExact avgt 20 8,237 ± 0,025 ns/op
b.InvocationBenchmark.handlePrimitiveInline avgt 20 2,467 ± 0,006 ns/op
b.InvocationBenchmark.handlePrimitiveBoxedInline avgt 20 9,966 ± 0,036 ns/op
b.InvocationBenchmark.handlePrimitiveExactInline avgt 20 2,462 ± 0,007 ns/op
b.InvocationBenchmark.handleUnreflectedExact avgt 20 30,176 ± 0,239 ns/op
b.InvocationBenchmark.handleUnreflectedExactPrivate avgt 20 29,882 ± 0,152 ns/op
b.InvocationBenchmark.handleUnreflectedPrimitiveExact avgt 20 8,765 ± 0,250 ns/op
b.InvocationBenchmark.handleUnreflectedExactInline avgt 20 28,594 ± 3,065 ns/op
b.InvocationBenchmark.handleUnreflectedExactPrivateInline avgt 20 39,853 ± 6,530 ns/op
b.InvocationBenchmark.handleUnreflectedPrimitiveExactInline avgt 20 2,539 ± 0,035 ns/op
# Run on Windows 8.1, x86-64 with Java1.8.0_25
Benchmark Mode Cnt Score Error Units
b.LookupBenchmark.reflection avgt 20 151,434 ± 15,701 ns/op
b.LookupBenchmark.handle avgt 20 913,461 ± 144,966 ns/op
b.LookupBenchmark.handlePreLookedUp avgt 20 784,532 ± 105,980 ns/op
# Run on Windows 8.1, x86-64 with Java1.8.0_25
@deepnighttwo
Copy link

where can I find the org.openjdk.jmh.annotation jar for JDK?

@UnLegitCode
Copy link

where can I find the org.openjdk.jmh.annotation jar for JDK?

Here is a link to the repository, clone it for yourself, and then build yourself the necessary module

https://github.com/openjdk/jmh/

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment