diff --git a/src/test/java/benchmark/CompletableFuturesBenchmark.java b/src/test/java/benchmark/CompletableFuturesBenchmark.java new file mode 100644 index 0000000000..e22bb1eb7f --- /dev/null +++ b/src/test/java/benchmark/CompletableFuturesBenchmark.java @@ -0,0 +1,108 @@ +package benchmark; + +import com.google.common.collect.ImmutableList; +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Level; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.Param; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.Warmup; +import org.openjdk.jmh.runner.Runner; +import org.openjdk.jmh.runner.options.Options; +import org.openjdk.jmh.runner.options.OptionsBuilder; + +import java.util.Collections; +import java.util.List; +import java.util.concurrent.CompletableFuture; + +@State(Scope.Benchmark) +@Warmup(iterations = 2, time = 1) +@Measurement(iterations = 3, time = 10, batchSize = 10) +@Fork(3) +public class CompletableFuturesBenchmark { + + + @Param({"2", "5"}) + public int depth; + public int howMany = 10; + + @Setup(Level.Trial) + public void setUp() { + } + + private List> mkCFObjects(int howMany, int depth) { + if (depth <= 0) { + return Collections.emptyList(); + } + ImmutableList.Builder> builder = ImmutableList.builder(); + for (int i = 0; i < howMany; i++) { + CompletableFuture cf = CompletableFuture.completedFuture(mkCFObjects(howMany, depth - 1)); + builder.add(cf); + } + return builder.build(); + } + + private List mkObjects(int howMany, int depth) { + if (depth <= 0) { + return Collections.emptyList(); + } + ImmutableList.Builder builder = ImmutableList.builder(); + for (int i = 0; i < howMany; i++) { + Object obj = mkObjects(howMany, depth - 1); + builder.add(obj); + } + return builder.build(); + } + + @Benchmark + @BenchmarkMode(Mode.Throughput) + public void benchmarkCFApproach() { + // make results + List> completableFutures = mkCFObjects(howMany, depth); + // traverse results + traverseCFS(completableFutures); + } + + @Benchmark + @BenchmarkMode(Mode.Throughput) + public void benchmarkMaterializedApproach() { + // make results + List objects = mkObjects(howMany, depth); + // traverse results + traverseObjects(objects); + } + + @SuppressWarnings("unchecked") + private void traverseCFS(List> completableFutures) { + for (CompletableFuture completableFuture : completableFutures) { + // and when it's done - visit its child results - which are always immediate on completed CFs + // so this whenComplete executed now + completableFuture.whenComplete((list, t) -> { + List> cfs = (List>) list; + traverseCFS(cfs); + }); + } + } + + @SuppressWarnings("unchecked") + private void traverseObjects(List objects) { + for (Object object : objects) { + List list = (List) object; + traverseObjects(list); + } + } + + public static void main(String[] args) throws Exception { + Options opt = new OptionsBuilder() + .include("benchmark.CompletableFuturesBenchmark") + .build(); + + new Runner(opt).run(); + } + +}