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 performance with dependence on threshold.
  41  */
  42 @OutputTimeUnit(TimeUnit.MINUTES)
  43 @State(Scope.Benchmark)
  44 public class ForkJoinPoolThresholdAutoSurplus {
  45 
  46     /**
  47      * Implementation notes:
  48      *
  49      * This test solves the problem with threshold = 1, and adaptive heuristics. The optimal level is static,
  50      * and lies somewhere in 1..2 interval. Note the test degrades significantly when heuristic starts to fail,
  51      * and the throughput is buried under FJP overheads.
  52      *
  53      * Baseline includes solving problem sequentially. Hence, each test provides the speedup for parallel execution
  54      * versus sequential version.
  55      */
  56 
  57     @Param("0")
  58     private int workers;
  59 
  60     @Param("10000000")
  61     private int size;
  62 
  63     @Param({"1", "2", "3", "4", "5", "6", "7", "8"})
  64     private int threshold;
  65 
  66     private ForkJoinPool fjp;
  67     private Problem problem;
  68 
  69     @Setup
  70     public void setup() {
  71         if (workers == 0) {
  72             workers = Runtime.getRuntime().availableProcessors();
  73         }
  74 
  75         problem = new Problem(size);
  76         fjp = new ForkJoinPool(workers);
  77     }
  78 
  79     @TearDown
  80     public void teardown() {
  81         fjp.shutdownNow();
  82     }
  83 
  84     @Benchmark
  85     public long baselineRaw() {
  86         return problem.solve();
  87     }
  88 
  89     @Benchmark
  90     public Long test() throws ExecutionException, InterruptedException {
  91         return fjp.invoke(new AutoQueuedTask(threshold, problem, 0, problem.size()));
  92     }
  93 
  94     private static class AutoQueuedTask extends RecursiveTask<Long> {
  95         private final int thr;
  96         private final Problem problem;
  97         private final int l;
  98         private final int r;
  99 
 100         public AutoQueuedTask(int thr, Problem p, int l, int r) {
 101             this.thr = thr;
 102             this.problem = p;
 103             this.l = l;
 104             this.r = r;
 105         }
 106 
 107         @Override
 108         protected Long compute() {
 109             if (r - l <= 1 || getSurplusQueuedTaskCount() >= thr) {
 110                 return problem.solve(l, r);
 111             }
 112 
 113             int mid = (l + r) >>> 1;
 114             ForkJoinTask<Long> t1 = new AutoQueuedTask(thr, problem, l, mid);
 115             ForkJoinTask<Long> t2 = new AutoQueuedTask(thr, problem, mid, r);
 116 
 117             t2.fork();
 118 
 119             long res = 0;
 120             res += t1.invoke();
 121             res += t2.join();
 122             return res;
 123         }
 124     }
 125 
 126 
 127 
 128 }