With more stable benchmark results
package javaslang; | |
import javaslang.collection.Array; | |
import javaslang.control.Option; | |
import org.junit.Test; | |
import org.openjdk.jmh.annotations.Benchmark; | |
import org.openjdk.jmh.annotations.Scope; | |
import org.openjdk.jmh.annotations.State; | |
import java.util.NoSuchElementException; | |
import java.util.Optional; | |
import java.util.stream.Stream; | |
import static javaslang.API.*; | |
import static javaslang.ForExpressionBenchmark.Variants.*; | |
/** | |
* Impl Score ± Error Unit java_exception java_option java_optional java_stream_option java_stream_optional slang_bind_option slang_option | |
* java_exception 192,309.05 ± 1.62% ops/s 0.04× 0.03× 0.10× 0.09× 0.06× 0.37× | |
* java_option 4,927,477.78 ± 0.42% ops/s 25.62× 0.87× 2.44× 2.21× 1.52× 9.55× | |
* java_optional 5,683,329.51 ± 2.70% ops/s 29.55× 1.15× 2.81× 2.55× 1.75× 11.02× | |
* java_stream_option 2,020,996.37 ± 0.91% ops/s 10.51× 0.41× 0.36× 0.91× 0.62× 3.92× | |
* java_stream_optional 2,226,325.04 ± 0.70% ops/s 11.58× 0.45× 0.39× 1.10× 0.69× 4.32× | |
* slang_bind_option 3,240,039.84 ± 0.90% ops/s 16.85× 0.66× 0.57× 1.60× 1.46× 6.28× | |
* slang_option 515,900.77 ± 0.95% ops/s 2.68× 0.10× 0.09× 0.26× 0.23× 0.16× | |
*/ | |
@State(Scope.Benchmark) | |
public class ForExpressionBenchmark { | |
private static final Array<Class<?>> CLASSES = Array(ForExpressionBenchmark.class); | |
@Test | |
public void testAsserts() { JmhRunner.runDebugWithAsserts(CLASSES); } | |
// public static void runVerySlowNoAsserts(Array<Class<?>> groups, Includes... includes) { | |
// run(20, 20, 1000, ForkJvm.ENABLE, VerboseMode.EXTRA, Assertions.DISABLE, PrintInlining.DISABLE, groups, includes).print(); | |
// } | |
public static void main(String... args) { | |
JmhRunner.runDebugWithAsserts(CLASSES); | |
JmhRunner.runVerySlowNoAsserts(CLASSES); | |
} | |
/*@formatter:off*/ | |
final GrillServiceWithOptional grillServiceWithOptional_all = new GrillServiceWithOptional(); | |
final GrillServiceWithOptional grillServiceWithOptional_no_charcoal = new GrillServiceWithOptional() { @Override Optional<Charcoal> charcoal() { return Optional.empty(); } }; | |
final GrillServiceWithOptional grillServiceWithOptional_no_lighter = new GrillServiceWithOptional() { @Override Optional<Lighter> lighter() { return Optional.empty(); } }; | |
final GrillServiceWithOptional grillServiceWithOptional_no_fire = new GrillServiceWithOptional() { @Override Optional<Fire> lightFire(Charcoal charcoal, Lighter lighter) { return Optional.empty(); } }; | |
final GrillServiceWithOptional grillServiceWithOptional_no_cornCob = new GrillServiceWithOptional() { @Override Optional<CornCob> cornCob() { return Optional.empty(); } }; | |
final GrillServiceWithOptional grillServiceWithOptional_no_grill = new GrillServiceWithOptional() { @Override Optional<Dinner> grill(Fire fire, CornCob cornCob) { return Optional.empty(); } }; | |
final GrillServiceWithOption grillServiceWithOption_all = new GrillServiceWithOption(); | |
final GrillServiceWithOption grillServiceWithOption_no_charcoal = new GrillServiceWithOption() { @Override Option<Charcoal> charcoal() { return None(); } }; | |
final GrillServiceWithOption grillServiceWithOption_no_lighter = new GrillServiceWithOption() { @Override Option<Lighter> lighter() { return None(); } }; | |
final GrillServiceWithOption grillServiceWithOption_no_fire = new GrillServiceWithOption() { @Override Option<Fire> lightFire(Charcoal charcoal, Lighter lighter) { return None(); } }; | |
final GrillServiceWithOption grillServiceWithOption_no_cornCob = new GrillServiceWithOption() { @Override Option<CornCob> cornCob() { return None(); } }; | |
final GrillServiceWithOption grillServiceWithOption_no_grill = new GrillServiceWithOption() { @Override Option<Dinner> grill(Fire fire, CornCob cornCob) { return None(); } }; | |
/*@formatter:on*/ | |
@Benchmark | |
public Object java_exception() { | |
final Optional<?> all = makeJavaDinner0(grillServiceWithOptional_all); | |
final Optional<?> noCharcoal = makeJavaDinner0(grillServiceWithOptional_no_charcoal); | |
final Optional<?> noLighter = makeJavaDinner0(grillServiceWithOptional_no_lighter); | |
final Optional<?> noFire = makeJavaDinner0(grillServiceWithOptional_no_fire); | |
final Optional<?> noCornCob = makeJavaDinner0(grillServiceWithOptional_no_cornCob); | |
final Optional<?> noGrill = makeJavaDinner0(grillServiceWithOptional_no_grill); | |
assert all.isPresent() && !noCharcoal.isPresent() && !noLighter.isPresent() && !noFire.isPresent() && !noCornCob.isPresent() && !noGrill.isPresent(); | |
return new Object[] { all, noCharcoal, noLighter, noFire, noCornCob, noGrill }; | |
} | |
@Benchmark | |
public Object java_optional() { | |
final Optional<?> all = makeJavaDinner(grillServiceWithOptional_all); | |
final Optional<?> noCharcoal = makeJavaDinner(grillServiceWithOptional_no_charcoal); | |
final Optional<?> noLighter = makeJavaDinner(grillServiceWithOptional_no_lighter); | |
final Optional<?> noFire = makeJavaDinner(grillServiceWithOptional_no_fire); | |
final Optional<?> noCornCob = makeJavaDinner(grillServiceWithOptional_no_cornCob); | |
final Optional<?> noGrill = makeJavaDinner(grillServiceWithOptional_no_grill); | |
assert all.isPresent() && !noCharcoal.isPresent() && !noLighter.isPresent() && !noFire.isPresent() && !noCornCob.isPresent() && !noGrill.isPresent(); | |
return new Object[] { all, noCharcoal, noLighter, noFire, noCornCob, noGrill }; | |
} | |
@Benchmark | |
public Object java_option() { | |
final Option<?> all = makeJavaDinner(grillServiceWithOption_all); | |
final Option<?> noCharcoal = makeJavaDinner(grillServiceWithOption_no_charcoal); | |
final Option<?> noLighter = makeJavaDinner(grillServiceWithOption_no_lighter); | |
final Option<?> noFire = makeJavaDinner(grillServiceWithOption_no_fire); | |
final Option<?> noCornCob = makeJavaDinner(grillServiceWithOption_no_cornCob); | |
final Option<?> noGrill = makeJavaDinner(grillServiceWithOption_no_grill); | |
assert all.isDefined() && noCharcoal.isEmpty() && noLighter.isEmpty() && noFire.isEmpty() && noCornCob.isEmpty() && noGrill.isEmpty(); | |
return new Object[] { all, noCharcoal, noLighter, noFire, noCornCob, noGrill }; | |
} | |
@Benchmark | |
public Object java_stream_option() { | |
final Option<?> all = makeJavaStreamDinner(grillServiceWithOption_all); | |
final Option<?> noCharcoal = makeJavaStreamDinner(grillServiceWithOption_no_charcoal); | |
final Option<?> noLighter = makeJavaStreamDinner(grillServiceWithOption_no_lighter); | |
final Option<?> noFire = makeJavaStreamDinner(grillServiceWithOption_no_fire); | |
final Option<?> noCornCob = makeJavaStreamDinner(grillServiceWithOption_no_cornCob); | |
final Option<?> noGrill = makeJavaStreamDinner(grillServiceWithOption_no_grill); | |
assert all.isDefined() && noCharcoal.isEmpty() && noLighter.isEmpty() && noFire.isEmpty() && noCornCob.isEmpty() && noGrill.isEmpty(); | |
return new Object[] { all, noCharcoal, noLighter, noFire, noCornCob, noGrill }; | |
} | |
@Benchmark | |
public Object java_stream_optional() { | |
final Optional<?> all = makeJavaStreamDinner(grillServiceWithOptional_all); | |
final Optional<?> noCharcoal = makeJavaStreamDinner(grillServiceWithOptional_no_charcoal); | |
final Optional<?> noLighter = makeJavaStreamDinner(grillServiceWithOptional_no_lighter); | |
final Optional<?> noFire = makeJavaStreamDinner(grillServiceWithOptional_no_fire); | |
final Optional<?> noCornCob = makeJavaStreamDinner(grillServiceWithOptional_no_cornCob); | |
final Optional<?> noGrill = makeJavaStreamDinner(grillServiceWithOptional_no_grill); | |
assert all.isPresent() && !noCharcoal.isPresent() && !noLighter.isPresent() && !noFire.isPresent() && !noCornCob.isPresent() && !noGrill.isPresent(); | |
return new Object[] { all, noCharcoal, noLighter, noFire, noCornCob, noGrill }; | |
} | |
@Benchmark | |
public Object slang_option() { | |
final Option<?> all = makeJavaslangDinner(grillServiceWithOption_all); | |
final Option<?> noCharcoal = makeJavaslangDinner(grillServiceWithOption_no_charcoal); | |
final Option<?> noLighter = makeJavaslangDinner(grillServiceWithOption_no_lighter); | |
final Option<?> noFire = makeJavaslangDinner(grillServiceWithOption_no_fire); | |
final Option<?> noCornCob = makeJavaslangDinner(grillServiceWithOption_no_cornCob); | |
final Option<?> noGrill = makeJavaslangDinner(grillServiceWithOption_no_grill); | |
assert all.isDefined() && noCharcoal.isEmpty() && noLighter.isEmpty() && noFire.isEmpty() && noCornCob.isEmpty() && noGrill.isEmpty(); | |
return new Object[] { all, noCharcoal, noLighter, noFire, noCornCob, noGrill }; | |
} | |
@Benchmark | |
public Object slang_bind_option() { | |
final Option<?> all = makeJavaslangBindDinner(grillServiceWithOption_all); | |
final Option<?> noCharcoal = makeJavaslangBindDinner(grillServiceWithOption_no_charcoal); | |
final Option<?> noLighter = makeJavaslangBindDinner(grillServiceWithOption_no_lighter); | |
final Option<?> noFire = makeJavaslangBindDinner(grillServiceWithOption_no_fire); | |
final Option<?> noCornCob = makeJavaslangBindDinner(grillServiceWithOption_no_cornCob); | |
final Option<?> noGrill = makeJavaslangBindDinner(grillServiceWithOption_no_grill); | |
assert all.isDefined() && noCharcoal.isEmpty() && noLighter.isEmpty() && noFire.isEmpty() && noCornCob.isEmpty() && noGrill.isEmpty(); | |
return new Object[] { all, noCharcoal, noLighter, noFire, noCornCob, noGrill }; | |
} | |
static class Variants { | |
/* benchmark Java solution using Exceptions */ | |
static Optional<Dinner> makeJavaDinner0(GrillServiceWithOptional service) { | |
try { | |
final Optional<Charcoal> charcoal = service.charcoal(); | |
final Optional<Lighter> lighter = service.lighter(); | |
final Optional<CornCob> cornCob = service.cornCob(); | |
final Optional<Fire> fire = service.lightFire(charcoal.get(), lighter.get()); | |
return service.grill(fire.get(), cornCob.get()); | |
} catch (NoSuchElementException e) { | |
return Optional.empty(); | |
} | |
} | |
/* benchmark Java solution using Optional */ | |
static Optional<Dinner> makeJavaDinner(GrillServiceWithOptional service) { | |
final Optional<Charcoal> charcoalOption = service.charcoal(); | |
final Optional<Lighter> lighterOption = service.lighter(); | |
if (charcoalOption.isPresent() && lighterOption.isPresent()) { | |
final Optional<Fire> fireOption = service.lightFire(charcoalOption.get(), lighterOption.get()); | |
final Optional<CornCob> cornCobOption = service.cornCob(); | |
if (fireOption.isPresent() && cornCobOption.isPresent()) { | |
return service.grill(fireOption.get(), cornCobOption.get()); | |
} | |
} | |
return Optional.empty(); | |
} | |
/* benchmark Java solution using Option */ | |
static Option<Dinner> makeJavaDinner(GrillServiceWithOption service) { | |
final Option<Charcoal> charcoalOption = service.charcoal(); | |
final Option<Lighter> lighterOption = service.lighter(); | |
if (charcoalOption.isDefined() && lighterOption.isDefined()) { | |
final Option<Fire> fireOption = service.lightFire(charcoalOption.get(), lighterOption.get()); | |
final Option<CornCob> cornCobOption = service.cornCob(); | |
if (fireOption.isDefined() && cornCobOption.isDefined()) { | |
return service.grill(fireOption.get(), cornCobOption.get()); | |
} | |
} | |
return None(); | |
} | |
/* make Java Stream dinner using Optional */ | |
static Optional<Dinner> makeJavaStreamDinner(GrillServiceWithOptional service) { | |
final Optional<Charcoal> charcoal = service.charcoal(); | |
final Optional<Lighter> lighter = service.lighter(); | |
final Optional<CornCob> cornCob = service.cornCob(); | |
return Stream.of(charcoal, lighter, cornCob).allMatch(Optional::isPresent) | |
? service.lightFire(charcoal.get(), lighter.get()).flatMap(fire -> service.grill(fire, cornCob.get())) | |
: Optional.empty(); | |
} | |
/* make Java Stream dinner using Option */ | |
static Option<Dinner> makeJavaStreamDinner(GrillServiceWithOption service) { | |
final Option<Charcoal> charcoal = service.charcoal(); | |
final Option<Lighter> lighter = service.lighter(); | |
final Option<CornCob> cornCob = service.cornCob(); | |
return Stream.of(charcoal, lighter, cornCob).allMatch(Option::isDefined) | |
? service.lightFire(charcoal.get(), lighter.get()).flatMap(fire -> service.grill(fire, cornCob.get())) | |
: None(); | |
} | |
/* benchmark Javaslang solution using Option */ | |
static Option<Dinner> makeJavaslangDinner(GrillServiceWithOption service) { | |
return For(service.charcoal(), charcoal | |
-> For(service.lighter(), lighter | |
-> For(service.lightFire(charcoal, lighter), fire | |
-> For(service.cornCob(), cornCob | |
-> For(service.grill(fire, cornCob)) | |
.yield())))).toOption(); | |
} | |
/* benchmark alternative Javaslang solution using Option */ | |
static <T1, T2, U> Option<U> Bind(Option<T1> o1, Option<T2> o2, Function2<? super T1, ? super T2, Option<U>> mapper) { | |
return o1.flatMap(v1 -> o2.flatMap(v2 -> mapper.apply(v1, v2))); | |
} | |
static Option<Dinner> makeJavaslangBindDinner(GrillServiceWithOption service) { | |
return Bind(service.charcoal(), service.lighter(), (charcoal, lighter) | |
-> Bind(service.lightFire(charcoal, lighter), service.cornCob(), (fire, cornCob) | |
-> service.grill(fire, cornCob))); | |
} | |
} | |
} | |
class GrillServiceWithOption { | |
Option<Charcoal> charcoal() { return Some(new Charcoal()); } | |
Option<Lighter> lighter() { return Some(new Lighter()); } | |
Option<Fire> lightFire(Charcoal charcoal, Lighter lighter) { return Some(new Fire(charcoal, lighter)); } | |
Option<CornCob> cornCob() { return Some(new CornCob());} | |
Option<Dinner> grill(Fire fire, CornCob cornCob) { return Some(new Dinner(fire, cornCob)); } | |
} | |
class GrillServiceWithOptional { | |
Optional<Charcoal> charcoal() { return Optional.of(new Charcoal()); } | |
Optional<Lighter> lighter() { return Optional.of(new Lighter()); } | |
Optional<Fire> lightFire(Charcoal charcoal, Lighter lighter) { return Optional.of(new Fire(charcoal, lighter)); } | |
Optional<CornCob> cornCob() { return Optional.of(new CornCob()); } | |
Optional<Dinner> grill(Fire fire, CornCob cornCob) { return Optional.of(new Dinner(fire, cornCob)); } | |
} | |
class Charcoal {} | |
class Lighter {} | |
class Fire { | |
final Charcoal charcoal; | |
final Lighter lighter; | |
Fire(Charcoal charcoal, Lighter lighter) { | |
this.charcoal = charcoal; | |
this.lighter = lighter; | |
} | |
} | |
class CornCob {} | |
class Dinner { | |
final Fire fire; | |
final CornCob cornCob; | |
Dinner(Fire fire, CornCob cornCob) { | |
this.fire = fire; | |
this.cornCob = cornCob; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment