Skip to content

Commit da289ec

Browse files
Merge pull request ReactiveX#963 from gvsmirnov/master
A more robust JMH benchmarking set-up
2 parents 27bf21c + 2fa35e5 commit da289ec

File tree

3 files changed

+114
-125
lines changed

3 files changed

+114
-125
lines changed

build.gradle

Lines changed: 51 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,20 @@ apply from: file('gradle/release.gradle')
99
buildscript {
1010
repositories {
1111
mavenLocal()
12-
mavenCentral() // maven { url 'http://jcenter.bintray.com' }
12+
mavenCentral()
13+
maven {
14+
//FIXME: waiting for https://github.com/johnrengelman/shadow/pull/38 to merge
15+
name 'Shadow'
16+
url 'http://dl.bintray.com/content/gvsmirnov/gradle-plugins'
17+
}
18+
jcenter()
19+
}
20+
21+
dependencies {
22+
// Required for benchmarks
23+
classpath 'com.github.jengelman.gradle.plugins:shadow:0.8.1'
1324
}
25+
1426
apply from: file('gradle/buildscript.gradle'), to: buildscript
1527
}
1628

@@ -19,7 +31,7 @@ allprojects {
1931
apply plugin: 'idea'
2032
repositories {
2133
mavenLocal()
22-
mavenCentral() // maven { url: 'http://jcenter.bintray.com/' }
34+
mavenCentral()
2335
}
2436
}
2537

@@ -31,57 +43,67 @@ subprojects {
3143
configurations {
3244
examplesCompile.extendsFrom compile
3345
examplesRuntime.extendsFrom runtime
34-
perfCompile.extendsFrom compile
35-
perfRuntime.extendsFrom runtime
3646
}
3747

3848

3949
tasks.withType(Javadoc).each {
4050
it.classpath = sourceSets.main.compileClasspath
4151
}
4252

43-
sourceSets {
44-
//include /src/examples folder
53+
sourceSets {
4554
examples
46-
//include /src/perf folder
47-
perf {
48-
java {
49-
srcDir 'src/perf/java'
50-
compileClasspath += main.output
51-
runtimeClasspath += main.output
52-
}
53-
}
54-
}
55-
56-
dependencies {
57-
perfCompile 'org.openjdk.jmh:jmh-core:0.2'
55+
perf
5856
}
5957

6058
tasks.build {
61-
//include 'examples' in build task
59+
//include 'examples' in build task
6260
dependsOn(examplesClasses)
63-
//include 'perf' in build task
64-
// dependsOn(perfClasses) //-> Not working so commented out
61+
}
62+
63+
dependencies {
64+
perfCompile 'org.openjdk.jmh:jmh-core:0.5.3'
65+
perfCompile 'org.openjdk.jmh:jmh-generator-annprocess:0.5.3'
66+
67+
perfCompile project
6568
}
6669

6770
eclipse {
68-
classpath {
69-
// include 'provided' dependencies on the classpath
70-
plusConfigurations += configurations.provided
71+
classpath {
7172
plusConfigurations += configurations.perfCompile
7273

7374
downloadSources = true
7475
downloadJavadoc = true
7576
}
7677
}
77-
78+
7879
idea {
7980
module {
80-
// include 'provided' dependencies on the classpath
81-
scopes.PROVIDED.plus += configurations.provided
82-
// TODO not sure what to add it to
83-
//scopes.PROVIDED.plus += configurations.perfCompile
81+
scopes.PROVIDED.plus += configurations.perfCompile
82+
scopes.PROVIDED.minus += configurations.compile
83+
}
84+
}
85+
86+
task perfJar(type: Jar, dependsOn: perfClasses) {
87+
from sourceSets.perf.output + sourceSets.main.output
88+
}
89+
90+
task benchmarks(dependsOn: perfJar) {
91+
92+
apply plugin: "shadow"
93+
94+
shadow {
95+
classifier = "benchmarks"
96+
includeDependenciesFor = ["runtime", "perfRuntime"]
97+
98+
transformer(com.github.jengelman.gradle.plugins.shadow.transformers.ManifestResourceTransformer) {
99+
mainClass = "org.openjdk.jmh.Main"
100+
}
84101
}
102+
103+
doLast {
104+
shadowJar.execute()
105+
}
106+
85107
}
86108
}
87109

rxjava-core/build.gradle

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
apply plugin: 'maven'
22
apply plugin: 'osgi'
3-
apply plugin:'application'
43

54
sourceCompatibility = JavaVersion.VERSION_1_6
65
targetCompatibility = JavaVersion.VERSION_1_6
@@ -13,7 +12,7 @@ dependencies {
1312
javadoc {
1413
// we do not want the org.rx.operations package include
1514
exclude '**/operations/**'
16-
15+
1716
options {
1817
doclet = "org.benjchristensen.doclet.DocletExclude"
1918
docletpath = [rootProject.file('./gradle/doclet-exclude.jar')]
@@ -31,11 +30,4 @@ jar {
3130
instruction 'Import-Package', '!org.junit,!junit.framework,!org.mockito.*,*'
3231
instruction 'Eclipse-ExtensibleAPI', 'true'
3332
}
34-
}
35-
36-
task time(type:JavaExec) {
37-
classpath = sourceSets.perf.runtimeClasspath
38-
group 'Application'
39-
description 'Execute the calipser benchmark timing of Rx'
40-
main 'rx.operators.ObservableBenchmark'
4133
}
Lines changed: 62 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,12 @@
11
package rx.operators;
22

3+
import java.util.ArrayList;
4+
import java.util.Collection;
35
import java.util.concurrent.CountDownLatch;
4-
import java.util.concurrent.atomic.AtomicInteger;
56

6-
import org.openjdk.jmh.annotations.GenerateMicroBenchmark;
7-
import org.openjdk.jmh.runner.Runner;
8-
import org.openjdk.jmh.runner.RunnerException;
9-
import org.openjdk.jmh.runner.options.Options;
10-
import org.openjdk.jmh.runner.options.OptionsBuilder;
7+
import org.openjdk.jmh.annotations.*;
118

9+
import org.openjdk.jmh.logic.BlackHole;
1210
import rx.Observable;
1311
import rx.Observable.OnSubscribe;
1412
import rx.Observable.Operator;
@@ -19,107 +17,84 @@
1917
public class ObservableBenchmark {
2018

2119
@GenerateMicroBenchmark
22-
public void timeBaseline() {
23-
observableOfInts.subscribe(newObserver());
24-
awaitAllObservers();
25-
}
26-
27-
@GenerateMicroBenchmark
28-
public int timeMapIterate() {
29-
int x = 0;
30-
for (int j = 0; j < intValues.length; j++) {
31-
// use hash code to make sure the JIT doesn't optimize too much and remove all of
32-
// our code.
33-
x |= ident.call(intValues[j]).hashCode();
20+
public void measureBaseline(BlackHole bh, Input input) {
21+
for (Integer value : input.values) {
22+
bh.consume(IDENTITY_FUNCTION.call(value));
3423
}
35-
return x;
3624
}
3725

3826
@GenerateMicroBenchmark
39-
public void timeMap() {
40-
timeOperator(new OperatorMap<Integer, Object>(ident));
27+
public void measureMap(Input input) throws InterruptedException {
28+
input.observable.lift(MAP_OPERATOR).subscribe(input.observer);
29+
30+
input.awaitCompletion();
4131
}
4232

43-
/**************************************************************************
44-
* Below is internal stuff to avoid object allocation and time overhead of anything that isn't
45-
* being tested.
46-
*
47-
* @throws RunnerException
48-
**************************************************************************/
33+
private static final Func1<Integer, Integer> IDENTITY_FUNCTION = new Func1<Integer, Integer>() {
34+
@Override
35+
public Integer call(Integer value) {
36+
return value;
37+
}
38+
};
4939

50-
public static void main(String[] args) throws RunnerException {
51-
Options opt = new OptionsBuilder()
52-
.include(ObservableBenchmark.class.getName()+".*")
53-
.forks(1)
54-
.build();
40+
private static final Operator<Integer, Integer> MAP_OPERATOR = new OperatorMap<Integer, Integer>(IDENTITY_FUNCTION);
5541

56-
new Runner(opt).run();
57-
}
42+
@State(Scope.Thread)
43+
public static class Input {
5844

59-
private void timeOperator(Operator<Object, Integer> op) {
60-
observableOfInts.lift(op).subscribe(newObserver());
61-
awaitAllObservers();
62-
}
45+
@Param({"1", "1024", "1048576"})
46+
public int size;
6347

64-
private final static AtomicInteger outstanding = new AtomicInteger(0);
65-
private final static CountDownLatch latch = new CountDownLatch(1);
48+
public Collection<Integer> values;
49+
public Observable<Integer> observable;
50+
public Observer<Integer> observer;
6651

67-
private static <T> Observer<T> newObserver() {
68-
outstanding.incrementAndGet();
69-
return new Observer<T>() {
70-
@Override
71-
public void onCompleted() {
72-
int left = outstanding.decrementAndGet();
73-
if (left == 0) {
74-
latch.countDown();
75-
}
52+
private CountDownLatch latch;
53+
54+
@Setup
55+
public void setup() {
56+
values = new ArrayList<Integer>();
57+
for(int i = 0; i < size; i ++) {
58+
values.add(i);
7659
}
7760

78-
@Override
79-
public void onError(Throwable e) {
80-
int left = outstanding.decrementAndGet();
81-
if (left == 0) {
61+
observable = Observable.create(new OnSubscribe<Integer>() {
62+
@Override
63+
public void call(Subscriber<? super Integer> o) {
64+
for (Integer value : values) {
65+
if (o.isUnsubscribed())
66+
return;
67+
o.onNext(value);
68+
}
69+
o.onCompleted();
70+
}
71+
});
72+
73+
final BlackHole bh = new BlackHole();
74+
latch = new CountDownLatch(1);
75+
76+
observer = new Observer<Integer>() {
77+
@Override
78+
public void onCompleted() {
8279
latch.countDown();
8380
}
84-
}
8581

86-
@Override
87-
public void onNext(T t) {
88-
// do nothing
89-
}
90-
};
91-
}
82+
@Override
83+
public void onError(Throwable e) {
84+
throw new RuntimeException(e);
85+
}
86+
87+
@Override
88+
public void onNext(Integer value) {
89+
bh.consume(value);
90+
}
91+
};
9292

93-
private static void awaitAllObservers() {
94-
try {
95-
latch.await();
96-
} catch (InterruptedException e) {
97-
return;
9893
}
99-
}
10094

101-
private static final Integer[] intValues = new Integer[1000];
102-
static {
103-
for (int i = 0; i < intValues.length; i++) {
104-
intValues[i] = i;
95+
public void awaitCompletion() throws InterruptedException {
96+
latch.await();
10597
}
10698
}
10799

108-
private static final Observable<Integer> observableOfInts = Observable.create(new OnSubscribe<Integer>() {
109-
@Override
110-
public void call(Subscriber<? super Integer> o) {
111-
for (int i = 0; i < intValues.length; i++) {
112-
if (o.isUnsubscribed())
113-
return;
114-
o.onNext(intValues[i]);
115-
}
116-
o.onCompleted();
117-
}
118-
});
119-
private static final Func1<Integer, Object> ident = new Func1<Integer, Object>() {
120-
@Override
121-
public Object call(Integer t) {
122-
return t;
123-
}
124-
};
125100
}

0 commit comments

Comments
 (0)