Skip to content

Commit b95468d

Browse files
committed
Add benchmark
0 parents  commit b95468d

File tree

8 files changed

+182
-0
lines changed

8 files changed

+182
-0
lines changed

.github/workflows/validate.yml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
name: Validate
2+
3+
on:
4+
push:
5+
6+
jobs:
7+
build:
8+
runs-on: ubuntu-latest
9+
10+
steps:
11+
- uses: actions/checkout@v4
12+
- run: mvn --no-transfer-progress test-compile

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
/target/
2+
/.settings
3+
.project
4+
.classpath

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
# Benchmarks to support ["Turbocharging Java Reflection Performance with MethodHandle" blog post](https://hazelcast.com/?p=40392).

pom.xml

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
<project xmlns="http://maven.apache.org/POM/4.0.0"
2+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
4+
<modelVersion>4.0.0</modelVersion>
5+
<groupId>com.hazelcast</groupId>
6+
<artifactId>reflective-blog-benchmark</artifactId>
7+
<version>0.0.1-SNAPSHOT</version>
8+
<properties>
9+
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
10+
<maven.compiler.source>17</maven.compiler.source>
11+
<maven.compiler.target>${maven.compiler.source}</maven.compiler.target>
12+
<version.jmh>1.37</version.jmh>
13+
</properties>
14+
15+
<build>
16+
<plugins>
17+
<plugin>
18+
<groupId>org.apache.maven.plugins</groupId>
19+
<artifactId>maven-compiler-plugin</artifactId>
20+
<version>3.11.0</version>
21+
</plugin>
22+
<plugin>
23+
<groupId>pw.krejci</groupId>
24+
<artifactId>jmh-maven-plugin</artifactId>
25+
<version>0.2.2</version>
26+
</plugin>
27+
</plugins>
28+
</build>
29+
30+
<dependencies>
31+
<dependency>
32+
<groupId>org.openjdk.jmh</groupId>
33+
<artifactId>jmh-core</artifactId>
34+
<version>${version.jmh}</version>
35+
<scope>test</scope>
36+
</dependency>
37+
<dependency>
38+
<groupId>org.openjdk.jmh</groupId>
39+
<artifactId>jmh-generator-annprocess</artifactId>
40+
<version>${version.jmh}</version>
41+
<scope>provided</scope>
42+
</dependency>
43+
</dependencies>
44+
</project>
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package benchmark;
2+
3+
import org.openjdk.jmh.annotations.*;
4+
import java.util.concurrent.TimeUnit;
5+
6+
@BenchmarkMode(Mode.AverageTime)
7+
@OutputTimeUnit(TimeUnit.NANOSECONDS)
8+
// For graphs, we want throughput results instead
9+
//@BenchmarkMode(Mode.Throughput)
10+
//@OutputTimeUnit(TimeUnit.SECONDS)
11+
@Warmup(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
12+
@Measurement(iterations = 3, time = 1, timeUnit = TimeUnit.SECONDS)
13+
@State(Scope.Benchmark)
14+
public abstract class AbstractReflectiveAccessBenchmark {
15+
protected SomeSource source = new SomeSource();
16+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
package benchmark;
2+
3+
import org.openjdk.jmh.annotations.*;
4+
import java.lang.invoke.*;
5+
import java.lang.reflect.Method;
6+
7+
public class PrimitiveReflectiveAccessBenchmark extends AbstractReflectiveAccessBenchmark {
8+
private Method method;
9+
private MethodHandle methodHandle;
10+
11+
@Setup
12+
public void setup() {
13+
try {
14+
method = source.getClass().getDeclaredMethod("getPrimitive");
15+
methodHandle = MethodHandles.lookup().unreflect(method);
16+
} catch (final ReflectiveOperationException e) {
17+
throw new RuntimeException(e);
18+
}
19+
}
20+
21+
@Benchmark
22+
public long direct() throws Throwable {
23+
return source.getPrimitive();
24+
}
25+
26+
@Benchmark
27+
public long reflection() throws ReflectiveOperationException {
28+
return ((Long) method.invoke(source)).longValue();
29+
}
30+
31+
@Benchmark
32+
public long methodHandleInvoke() throws Throwable {
33+
return ((Long) methodHandle.invoke(source)).longValue();
34+
}
35+
36+
@Benchmark
37+
public long methodHandleInvokeExact() throws Throwable {
38+
return (long) methodHandle.invokeExact(source);
39+
}
40+
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
package benchmark;
2+
3+
import org.openjdk.jmh.annotations.*;
4+
import java.lang.invoke.*;
5+
import java.lang.reflect.Method;
6+
import java.util.function.Function;
7+
8+
public class ReferenceReflectiveAccessBenchmark extends AbstractReflectiveAccessBenchmark {
9+
private Method method;
10+
private MethodHandle methodHandle;
11+
private Function<SomeSource, ?> lambdaMetafactoryFunction;
12+
13+
@Setup
14+
public void setup() {
15+
try {
16+
final MethodHandles.Lookup lookup = MethodHandles.lookup();
17+
18+
method = source.getClass().getDeclaredMethod("getObject");
19+
20+
methodHandle = lookup.unreflect(method);
21+
22+
lambdaMetafactoryFunction = (Function<SomeSource, ?>) LambdaMetafactory
23+
.metafactory(lookup, "apply", MethodType.methodType(Function.class),
24+
MethodType.methodType(Object.class, Object.class), methodHandle, methodHandle.type())
25+
.getTarget().invokeExact();
26+
} catch (final Throwable t) {
27+
throw new ExceptionInInitializerError(t);
28+
}
29+
}
30+
31+
@Benchmark
32+
public Object direct() throws Throwable {
33+
return source.getObject();
34+
}
35+
36+
@Benchmark
37+
public Object reflection() throws ReflectiveOperationException {
38+
return method.invoke(source);
39+
}
40+
41+
@Benchmark
42+
public Object methodHandle() throws Throwable {
43+
return methodHandle.invoke(source);
44+
}
45+
46+
@Benchmark
47+
public Object lambdaMetafactory() {
48+
return lambdaMetafactoryFunction.apply(source);
49+
}
50+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package benchmark;
2+
3+
/** Some dummy class to query metrics for using reflection */
4+
class SomeSource {
5+
private static long counter = Long.MAX_VALUE;
6+
7+
long getPrimitive() {
8+
// Not thread-safe but not relevant for our purposes
9+
return counter--;
10+
}
11+
12+
Object getObject() {
13+
return getPrimitive();
14+
}
15+
}

0 commit comments

Comments
 (0)