Skip to content

Commit f9fa09a

Browse files
committed
provides refactoring of benchmarks (#722)
* moves all the benchmarks to a separate submodule; * provides an ability to run baseline comparison benches Signed-off-by: Oleh Dokuka <[email protected]>
1 parent 6ef9986 commit f9fa09a

16 files changed

+270
-64
lines changed

benchmarks/README.md

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
## Usage of JMH tasks
2+
3+
Only execute specific benchmark(s) (wildcards are added before and after):
4+
```
5+
../gradlew jmh --include="(BenchmarkPrimary|OtherBench)"
6+
```
7+
If you want to specify the wildcards yourself, you can pass the full regexp:
8+
```
9+
../gradlew jmh --fullInclude=.*MyBenchmark.*
10+
```
11+
12+
Specify extra profilers:
13+
```
14+
../gradlew jmh --profilers="gc,stack"
15+
```
16+
17+
Prominent profilers (for full list call `jmhProfilers` task):
18+
- comp - JitCompilations, tune your iterations
19+
- stack - which methods used most time
20+
- gc - print garbage collection stats
21+
- hs_thr - thread usage
22+
23+
Change report format from JSON to one of [CSV, JSON, NONE, SCSV, TEXT]:
24+
```
25+
./gradlew jmh --format=csv
26+
```
27+
28+
Specify JVM arguments:
29+
```
30+
../gradlew jmh --jvmArgs="-Dtest.cluster=local"
31+
```
32+
33+
Run in verification mode (execute benchmarks with minimum of fork/warmup-/benchmark-iterations):
34+
```
35+
../gradlew jmh --verify=true
36+
```
37+
38+
## Comparing with the baseline
39+
If you wish you run two sets of benchmarks, one for the current change and another one for the "baseline",
40+
there is an additional task `jmhBaseline` that will use the latest release:
41+
```
42+
../gradlew jmh jmhBaseline --include=MyBenchmark
43+
```
44+
45+
## Resources
46+
- http://tutorials.jenkov.com/java-performance/jmh.html (Introduction)
47+
- http://hg.openjdk.java.net/code-tools/jmh/file/tip/jmh-samples/src/main/java/org/openjdk/jmh/samples/ (Samples)

benchmarks/build.gradle

Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
apply plugin: 'java'
2+
apply plugin: 'idea'
3+
4+
configurations {
5+
current
6+
baseline {
7+
resolutionStrategy.cacheChangingModulesFor 0, 'seconds'
8+
}
9+
}
10+
11+
dependencies {
12+
// Use the baseline to avoid using new APIs in the benchmarks
13+
compileOnly "io.rsocket:rsocket-core:${perfBaselineVersion}"
14+
compileOnly "io.rsocket:rsocket-transport-local:${perfBaselineVersion}"
15+
16+
implementation "org.openjdk.jmh:jmh-core:1.21"
17+
annotationProcessor "org.openjdk.jmh:jmh-generator-annprocess:1.21"
18+
19+
current project(':rsocket-core')
20+
current project(':rsocket-transport-local')
21+
baseline "io.rsocket:rsocket-core:${perfBaselineVersion}", {
22+
changing = true
23+
}
24+
baseline "io.rsocket:rsocket-transport-local:${perfBaselineVersion}", {
25+
changing = true
26+
}
27+
}
28+
29+
task jmhProfilers(type: JavaExec, description:'Lists the available profilers for the jmh task', group: 'Development') {
30+
classpath = sourceSets.main.runtimeClasspath
31+
main = 'org.openjdk.jmh.Main'
32+
args '-lprof'
33+
}
34+
35+
task jmh(type: JmhExecTask, description: 'Executing JMH benchmarks') {
36+
classpath = sourceSets.main.runtimeClasspath + configurations.current
37+
}
38+
39+
task jmhBaseline(type: JmhExecTask, description: 'Executing JMH baseline benchmarks') {
40+
classpath = sourceSets.main.runtimeClasspath + configurations.baseline
41+
}
42+
43+
class JmhExecTask extends JavaExec {
44+
45+
private String include;
46+
private String fullInclude;
47+
private String exclude;
48+
private String format = "json";
49+
private String profilers;
50+
private String jmhJvmArgs;
51+
private String verify;
52+
53+
public JmhExecTask() {
54+
super();
55+
}
56+
57+
public String getInclude() {
58+
return include;
59+
}
60+
61+
@Option(option = "include", description="configure bench inclusion using substring")
62+
public void setInclude(String include) {
63+
this.include = include;
64+
}
65+
66+
public String getFullInclude() {
67+
return fullInclude;
68+
}
69+
70+
@Option(option = "fullInclude", description = "explicitly configure bench inclusion using full JMH style regexp")
71+
public void setFullInclude(String fullInclude) {
72+
this.fullInclude = fullInclude;
73+
}
74+
75+
public String getExclude() {
76+
return exclude;
77+
}
78+
79+
@Option(option = "exclude", description = "explicitly configure bench exclusion using full JMH style regexp")
80+
public void setExclude(String exclude) {
81+
this.exclude = exclude;
82+
}
83+
84+
public String getFormat() {
85+
return format;
86+
}
87+
88+
@Option(option = "format", description = "configure report format")
89+
public void setFormat(String format) {
90+
this.format = format;
91+
}
92+
93+
public String getProfilers() {
94+
return profilers;
95+
}
96+
97+
@Option(option = "profilers", description = "configure jmh profiler(s) to use, comma separated")
98+
public void setProfilers(String profilers) {
99+
this.profilers = profilers;
100+
}
101+
102+
public String getJmhJvmArgs() {
103+
return jmhJvmArgs;
104+
}
105+
106+
@Option(option = "jvmArgs", description = "configure additional JMH JVM arguments, comma separated")
107+
public void setJmhJvmArgs(String jvmArgs) {
108+
this.jmhJvmArgs = jvmArgs;
109+
}
110+
111+
public String getVerify() {
112+
return verify;
113+
}
114+
115+
@Option(option = "verify", description = "run in verify mode")
116+
public void setVerify(String verify) {
117+
this.verify = verify;
118+
}
119+
120+
@TaskAction
121+
public void exec() {
122+
setMain("org.openjdk.jmh.Main");
123+
File resultFile = getProject().file("build/reports/" + getName() + "/result." + format);
124+
125+
if (include != null) {
126+
args(".*" + include + ".*");
127+
}
128+
else if (fullInclude != null) {
129+
args(fullInclude);
130+
}
131+
132+
if(exclude != null) {
133+
args("-e", exclude);
134+
}
135+
if(verify != null) { // execute benchmarks with the minimum amount of execution (only to check if they are working)
136+
System.out.println("Running in verify mode");
137+
args("-f", 1);
138+
args("-wi", 1);
139+
args("-i", 1);
140+
}
141+
args("-foe", "true"); //fail-on-error
142+
args("-v", "NORMAL"); //verbosity [SILENT, NORMAL, EXTRA]
143+
if(profilers != null) {
144+
for (String prof : profilers.split(",")) {
145+
args("-prof", prof);
146+
}
147+
}
148+
args("-jvmArgsPrepend", "-Xmx3072m");
149+
args("-jvmArgsPrepend", "-Xms3072m");
150+
if(jmhJvmArgs != null) {
151+
for(String jvmArg : jmhJvmArgs.split(" ")) {
152+
args("-jvmArgsPrepend", jvmArg);
153+
}
154+
}
155+
args("-rf", format);
156+
args("-rff", resultFile);
157+
158+
System.out.println("\nExecuting JMH with: " + getArgs() + "\n");
159+
resultFile.getParentFile().mkdirs();
160+
161+
super.exec();
162+
}
163+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package io.rsocket;
2+
3+
import org.openjdk.jmh.infra.Blackhole;
4+
5+
public class PayloadsPerfSubscriber extends PerfSubscriber<Payload> {
6+
7+
public PayloadsPerfSubscriber(Blackhole blackhole) {
8+
super(blackhole);
9+
}
10+
11+
@Override
12+
public void onNext(Payload payload) {
13+
payload.release();
14+
super.onNext(payload);
15+
}
16+
}

rsocket-core/src/jmh/java/io/rsocket/PerfSubscriber.java renamed to benchmarks/src/main/java/io/rsocket/PerfSubscriber.java

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@
55
import org.reactivestreams.Subscription;
66
import reactor.core.CoreSubscriber;
77

8-
public class PerfSubscriber implements CoreSubscriber<Payload> {
8+
public class PerfSubscriber<T> implements CoreSubscriber<T> {
99

10-
final CountDownLatch latch = new CountDownLatch(1);
10+
public final CountDownLatch latch = new CountDownLatch(1);
1111
final Blackhole blackhole;
1212

1313
Subscription s;
@@ -23,8 +23,7 @@ public void onSubscribe(Subscription s) {
2323
}
2424

2525
@Override
26-
public void onNext(Payload payload) {
27-
payload.release();
26+
public void onNext(T payload) {
2827
blackhole.consume(payload);
2928
s.request(1);
3029
}

rsocket-core/src/jmh/java/io/rsocket/RSocketPerf.java renamed to benchmarks/src/main/java/io/rsocket/RSocketPerf.java

Lines changed: 35 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,20 @@
44
import io.rsocket.transport.local.LocalClientTransport;
55
import io.rsocket.transport.local.LocalServerTransport;
66
import io.rsocket.util.EmptyPayload;
7+
import java.lang.reflect.Field;
8+
import java.util.Queue;
9+
import java.util.concurrent.locks.LockSupport;
710
import java.util.stream.IntStream;
811
import org.openjdk.jmh.annotations.Benchmark;
912
import org.openjdk.jmh.annotations.BenchmarkMode;
1013
import org.openjdk.jmh.annotations.Fork;
14+
import org.openjdk.jmh.annotations.Level;
1115
import org.openjdk.jmh.annotations.Measurement;
1216
import org.openjdk.jmh.annotations.Mode;
1317
import org.openjdk.jmh.annotations.Scope;
1418
import org.openjdk.jmh.annotations.Setup;
1519
import org.openjdk.jmh.annotations.State;
20+
import org.openjdk.jmh.annotations.TearDown;
1621
import org.openjdk.jmh.annotations.Warmup;
1722
import org.openjdk.jmh.infra.Blackhole;
1823
import org.reactivestreams.Publisher;
@@ -36,11 +41,26 @@ public class RSocketPerf {
3641

3742
RSocket client;
3843
Closeable server;
44+
Queue clientsQueue;
45+
46+
@TearDown
47+
public void tearDown() {
48+
client.dispose();
49+
server.dispose();
50+
}
51+
52+
@TearDown(Level.Iteration)
53+
public void awaitToBeConsumed() {
54+
while (!clientsQueue.isEmpty()) {
55+
LockSupport.parkNanos(1000);
56+
}
57+
}
3958

4059
@Setup
41-
public void setUp() {
60+
public void setUp() throws NoSuchFieldException, IllegalAccessException {
4261
server =
4362
RSocketFactory.receive()
63+
.frameDecoder(PayloadDecoder.ZERO_COPY)
4464
.acceptor(
4565
(setup, sendingSocket) ->
4666
Mono.just(
@@ -75,35 +95,41 @@ public Flux<Payload> requestChannel(Publisher<Payload> payloads) {
7595

7696
client =
7797
RSocketFactory.connect()
98+
.singleSubscriberRequester()
7899
.frameDecoder(PayloadDecoder.ZERO_COPY)
79100
.transport(LocalClientTransport.create("server"))
80101
.start()
81102
.block();
103+
104+
Field sendProcessorField = RSocketRequester.class.getDeclaredField("sendProcessor");
105+
sendProcessorField.setAccessible(true);
106+
107+
clientsQueue = (Queue) sendProcessorField.get(client);
82108
}
83109

84110
@Benchmark
85111
@SuppressWarnings("unchecked")
86-
public PerfSubscriber fireAndForget(Blackhole blackhole) throws InterruptedException {
87-
PerfSubscriber subscriber = new PerfSubscriber(blackhole);
112+
public PayloadsPerfSubscriber fireAndForget(Blackhole blackhole) throws InterruptedException {
113+
PayloadsPerfSubscriber subscriber = new PayloadsPerfSubscriber(blackhole);
88114
client.fireAndForget(PAYLOAD).subscribe((CoreSubscriber) subscriber);
89115
subscriber.latch.await();
90116

91117
return subscriber;
92118
}
93119

94120
@Benchmark
95-
public PerfSubscriber requestResponse(Blackhole blackhole) throws InterruptedException {
96-
PerfSubscriber subscriber = new PerfSubscriber(blackhole);
121+
public PayloadsPerfSubscriber requestResponse(Blackhole blackhole) throws InterruptedException {
122+
PayloadsPerfSubscriber subscriber = new PayloadsPerfSubscriber(blackhole);
97123
client.requestResponse(PAYLOAD).subscribe(subscriber);
98124
subscriber.latch.await();
99125

100126
return subscriber;
101127
}
102128

103129
@Benchmark
104-
public PerfSubscriber requestStreamWithRequestByOneStrategy(Blackhole blackhole)
130+
public PayloadsPerfSubscriber requestStreamWithRequestByOneStrategy(Blackhole blackhole)
105131
throws InterruptedException {
106-
PerfSubscriber subscriber = new PerfSubscriber(blackhole);
132+
PayloadsPerfSubscriber subscriber = new PayloadsPerfSubscriber(blackhole);
107133
client.requestStream(PAYLOAD).subscribe(subscriber);
108134
subscriber.latch.await();
109135

@@ -121,9 +147,9 @@ public MaxPerfSubscriber requestStreamWithRequestAllStrategy(Blackhole blackhole
121147
}
122148

123149
@Benchmark
124-
public PerfSubscriber requestChannelWithRequestByOneStrategy(Blackhole blackhole)
150+
public PayloadsPerfSubscriber requestChannelWithRequestByOneStrategy(Blackhole blackhole)
125151
throws InterruptedException {
126-
PerfSubscriber subscriber = new PerfSubscriber(blackhole);
152+
PayloadsPerfSubscriber subscriber = new PayloadsPerfSubscriber(blackhole);
127153
client.requestChannel(PAYLOAD_FLUX).subscribe(subscriber);
128154
subscriber.latch.await();
129155

gradle.properties

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,4 @@
1212
# limitations under the License.
1313
#
1414
version=1.0.0-RC6
15+
perfBaselineVersion=1.0.0-RC5
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
distributionBase=GRADLE_USER_HOME
22
distributionPath=wrapper/dists
3-
distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-bin.zip
3+
distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.3-bin.zip
44
zipStoreBase=GRADLE_USER_HOME
55
zipStorePath=wrapper/dists

rsocket-core/build.gradle

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,4 @@ dependencies {
4646
testRuntimeOnly 'org.junit.vintage:junit-vintage-engine'
4747
}
4848

49-
description = "Core functionality for the RSocket library"
50-
51-
apply from: 'jmh.gradle'
49+
description = "Core functionality for the RSocket library"

0 commit comments

Comments
 (0)