1 /*
   2  * Copyright (c) 2014 Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 package org.openjdk.bench.java.util.concurrent;
  24 
  25 import org.openjdk.jmh.annotations.Benchmark;
  26 import org.openjdk.jmh.annotations.OutputTimeUnit;
  27 import org.openjdk.jmh.annotations.Param;
  28 import org.openjdk.jmh.annotations.Scope;
  29 import org.openjdk.jmh.annotations.Setup;
  30 import org.openjdk.jmh.annotations.State;
  31 import org.openjdk.jmh.annotations.TearDown;
  32 
  33 import java.util.concurrent.ExecutionException;
  34 import java.util.concurrent.ForkJoinPool;
  35 import java.util.concurrent.ForkJoinTask;
  36 import java.util.concurrent.RecursiveTask;
  37 import java.util.concurrent.TimeUnit;
  38 
  39 /**
  40  * Benchmark assesses ForkJoinPool forking infrastructure.
  41  *
  42  * @author Aleksey Shipilev (aleksey.shipilev@oracle.com)
  43  */
  44 @OutputTimeUnit(TimeUnit.MINUTES)
  45 @State(Scope.Benchmark)
  46 public class ForkJoinPoolForking {
  47 
  48     /**
  49      * Implementation notes:
  50      *
  51      * This test harnesses forking infrastructure within FJP.
  52      * As such, no slack is given for allocating any humble number of tasks: the goal is to fork a lot.
  53      * The approximate number of tasks is (SIZE / THRESHOLD).
  54      *
  55      * Raw baseline gives the idea for compute bound for this benchmark.
  56      * FJP could be faster than baseline, because the baseline is single-threaded.
  57      */
  58 
  59     @Param("0")
  60     private int workers;
  61 
  62     @Param("10000000")
  63     private int size;
  64 
  65     @Param("10")
  66     private int threshold;
  67 
  68     private Problem problem;
  69     private ForkJoinPool fjpSync;
  70     private ForkJoinPool fjpAsync;
  71 
  72     @Setup
  73     public void setup() {
  74         problem = new Problem(size);
  75         if (workers == 0) {
  76             workers = Runtime.getRuntime().availableProcessors();
  77         }
  78         fjpSync = new ForkJoinPool(workers, ForkJoinPool.defaultForkJoinWorkerThreadFactory, null, false);
  79         fjpAsync = new ForkJoinPool(workers, ForkJoinPool.defaultForkJoinWorkerThreadFactory, null, true);
  80     }
  81 
  82     @TearDown
  83     public void teardown() {
  84         fjpSync.shutdownNow();
  85         fjpAsync.shutdownNow();
  86     }
  87 
  88     @Benchmark
  89     public long baselineRaw() {
  90         return problem.solve();
  91     }
  92 
  93     @Benchmark
  94     public Long testExplicit_Sync() throws ExecutionException, InterruptedException {
  95         return fjpSync.invoke(new ExplicitTask(problem, 0, problem.size(), threshold));
  96     }
  97 
  98     @Benchmark
  99     public Long testExplicit_Async() throws ExecutionException, InterruptedException {
 100         return fjpAsync.invoke(new ExplicitTask(problem, 0, problem.size(), threshold));
 101     }
 102 
 103     @Benchmark
 104     public Long testStandard_Sync() throws ExecutionException, InterruptedException {
 105         return fjpSync.invoke(new StandardTask(problem, 0, problem.size(), threshold));
 106     }
 107 
 108     @Benchmark
 109     public Long testStandard_Async() throws ExecutionException, InterruptedException {
 110         return fjpAsync.invoke(new StandardTask(problem, 0, problem.size(), threshold));
 111     }
 112 
 113     private static class ExplicitTask extends RecursiveTask<Long> {
 114         private final Problem problem;
 115         private final int l;
 116         private final int r;
 117         private final int thresh;
 118 
 119         public ExplicitTask(Problem p, int l, int r, int thresh) {
 120             this.problem = p;
 121             this.l = l;
 122             this.r = r;
 123             this.thresh = thresh;
 124         }
 125 
 126         @Override
 127         protected Long compute() {
 128             if (r - l <= thresh) {
 129                 return problem.solve(l, r);
 130             }
 131 
 132             int mid = (l + r) >>> 1;
 133             ForkJoinTask<Long> t1 = new ExplicitTask(problem, l, mid, thresh);
 134             ForkJoinTask<Long> t2 = new ExplicitTask(problem, mid, r, thresh);
 135 
 136             t1.fork();
 137             t2.fork();
 138 
 139             long res = 0;
 140             res += t2.join();
 141             res += t1.join();
 142             return res;
 143         }
 144     }
 145 
 146     private static class StandardTask extends RecursiveTask<Long> {
 147         private final Problem problem;
 148         private final int l;
 149         private final int r;
 150         private final int thresh;
 151 
 152         public StandardTask(Problem p, int l, int r, int thresh) {
 153             this.problem = p;
 154             this.l = l;
 155             this.r = r;
 156             this.thresh = thresh;
 157         }
 158 
 159         @Override
 160         protected Long compute() {
 161             if (r - l <= thresh) {
 162                 return problem.solve(l, r);
 163             }
 164 
 165             int mid = (l + r) >>> 1;
 166             ForkJoinTask<Long> t1 = new StandardTask(problem, l, mid, thresh);
 167             ForkJoinTask<Long> t2 = new StandardTask(problem, mid, r, thresh);
 168 
 169             ForkJoinTask.invokeAll(t1, t2);
 170             long res = 0;
 171             res += t1.join();
 172             res += t2.join();
 173             return res;
 174         }
 175     }
 176 
 177 
 178 }