1 /*
   2  * Copyright (c) 2005, 2013, 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.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 package org.openjdk.jmh.runner;
  26 
  27 
  28 import org.openjdk.jmh.infra.BenchmarkParams;
  29 import org.openjdk.jmh.infra.IterationParams;
  30 import org.openjdk.jmh.infra.ThreadParams;
  31 import org.openjdk.jmh.results.IterationResult;
  32 import org.openjdk.jmh.results.Result;
  33 import org.openjdk.jmh.runner.format.OutputFormat;
  34 import org.openjdk.jmh.runner.options.Options;
  35 import org.openjdk.jmh.runner.options.TimeValue;
  36 
  37 import java.lang.reflect.Method;
  38 import java.util.Collection;
  39 import java.util.HashMap;
  40 import java.util.Map;
  41 import java.util.concurrent.Callable;
  42 import java.util.concurrent.CountDownLatch;
  43 import java.util.concurrent.ExecutionException;
  44 import java.util.concurrent.Future;
  45 import java.util.concurrent.TimeUnit;
  46 import java.util.concurrent.TimeoutException;
  47 
  48 /**
  49  * Handler for a single benchmark.
  50  *
  51  * Handles name and execution information (# iterations, etc).
  52  * Executes the benchmark according to above parameters.
  53  */
  54 class LoopBenchmarkHandler extends BaseBenchmarkHandler {
  55 
  56     private final Method method;
  57 
  58     LoopBenchmarkHandler(OutputFormat format, Class<?> clazz, Method method, Options options, BenchmarkParams executionParams) {
  59         super(format, clazz, options, executionParams);
  60         this.method = method;
  61     }
  62 
  63     @Override
  64     public IterationResult runIteration(BenchmarkParams benchmarkParams, IterationParams params, boolean last) {
  65         int numThreads = benchmarkParams.getThreads();
  66         TimeValue runtime = params.getTime();
  67 
  68         CountDownLatch preSetupBarrier = new CountDownLatch(numThreads);
  69         CountDownLatch preTearDownBarrier = new CountDownLatch(numThreads);
  70 
  71         // result object to accumulate the results in
  72         IterationResult iterationResults = new IterationResult(benchmarkParams, params);
  73 
  74         InfraControl control = new InfraControl(benchmarkParams, params, preSetupBarrier, preTearDownBarrier, last);
  75 
  76         // preparing the worker runnables
  77         BenchmarkTask[] runners = new BenchmarkTask[numThreads];
  78 
  79         ThreadParams[] threadParamses = distributeThreads(numThreads, benchmarkParams.getThreadGroups());
  80         for (int i = 0; i < runners.length; i++) {
  81             runners[i] = new BenchmarkTask(control, threadParamses[i]);
  82         }
  83 
  84         // profilers start way before the workload starts to capture
  85         // the edge behaviors.
  86         startProfilers(benchmarkParams, params);
  87 
  88         // submit tasks to threadpool
  89         Map<BenchmarkTask, Future<Collection<? extends Result>>> results = new HashMap<BenchmarkTask, Future<Collection<? extends Result>>>();
  90         for (BenchmarkTask runner : runners) {
  91             results.put(runner, executor.submit(runner));
  92         }
  93 
  94         // wait for all workers to transit to measurement
  95         while (control.warmupShouldWait) {
  96             try {
  97                 TimeUnit.MILLISECONDS.sleep(100);
  98             } catch (InterruptedException e) {
  99                 // ignore
 100             }
 101         }
 102 
 103         // wait for the iteration time to expire
 104         switch (benchmarkParams.getMode()) {
 105             case SingleShotTime:
 106                 // don't wait here, block on timed result Future
 107                 break;
 108             default:
 109                 try {
 110                     runtime.sleep();
 111                 } catch (InterruptedException e) {
 112                     // regardless...
 113                 }
 114         }
 115 
 116         // now we communicate all worker threads should stop
 117         control.isDone = true;
 118 
 119         // wait for all workers to transit to teardown
 120         while (control.warmdownShouldWait) {
 121             try {
 122                 TimeUnit.MILLISECONDS.sleep(100);
 123             } catch (InterruptedException e) {
 124                 // ignore
 125             }
 126         }
 127 
 128         // Adjust waiting intervals:
 129         //  - We don't know the running time for SingleShot benchmarks,
 130         //    we wait for at least 10 minutes for benchmark to stop; this
 131         //    can be adjusted with usual warmup/measurement duration settings;
 132         //  - For other benchmarks, we wait for twice the run time,
 133         //    but at least 5 seconds to cover for low run times.
 134         long timeToWait;
 135         switch (benchmarkParams.getMode()) {
 136             case SingleShotTime:
 137                 timeToWait = Math.max(TimeUnit.SECONDS.toNanos(600), runtime.convertTo(TimeUnit.NANOSECONDS));
 138                 break;
 139             default:
 140                 timeToWait = Math.max(runtime.convertTo(TimeUnit.NANOSECONDS) * 2, TimeUnit.SECONDS.toNanos(5));
 141         }
 142 
 143         // Wait for the result, continuously polling the worker threads.
 144         // The abrupt exception in any worker will float up here.
 145         int expected = numThreads;
 146         while (expected > 0) {
 147             for (BenchmarkTask task : results.keySet()) {
 148                 Future<Collection<? extends Result>> fr = results.get(task);
 149                 try {
 150                     fr.get(timeToWait, TimeUnit.NANOSECONDS);
 151                     expected--;
 152                 } catch (InterruptedException ex) {
 153                     throw new BenchmarkException(ex);
 154                 } catch (ExecutionException ex) {
 155                     // unwrap: ExecutionException -> Throwable-wrapper -> InvocationTargetException
 156                     Throwable cause = ex.getCause().getCause().getCause();
 157                     throw new BenchmarkException(cause);
 158                 } catch (TimeoutException e) {
 159                     // try to kick the thread, if it was already started
 160                     Thread runner = task.runner;
 161                     if (runner != null) {
 162                         out.print("(*interrupt*) ");
 163                         runner.interrupt();
 164                     }
 165                 }
 166             }
 167         }
 168 
 169         // Get the results.
 170         // Should previous loop allow us to get to this point, we can fully expect
 171         // all the results ready without the exceptions.
 172         for (Future<Collection<? extends Result>> fr : results.values()) {
 173             try {
 174                 iterationResults.addResults(fr.get());
 175             } catch (InterruptedException ex) {
 176                 throw new IllegalStateException("Impossible to be here");
 177             } catch (ExecutionException ex) {
 178                 throw new IllegalStateException("Impossible to be here");
 179             }
 180         }
 181 
 182         // profilers stop when after all threads are confirmed to be
 183         // finished to capture the edge behaviors
 184         stopProfilers(benchmarkParams, params, iterationResults);
 185 
 186         return iterationResults;
 187     }
 188 
 189     /**
 190      * Worker body.
 191      */
 192     class BenchmarkTask implements Callable<Collection<? extends Result>> {
 193 
 194         private volatile Thread runner;
 195         private final InfraControl control;
 196         private final ThreadParams threadParams;
 197 
 198         BenchmarkTask(InfraControl control, ThreadParams threadParams) {
 199             this.control = control;
 200             this.threadParams = threadParams;
 201         }
 202 
 203         @Override
 204         public Collection<? extends Result> call() throws Exception {
 205             try {
 206                 // bind the executor thread
 207                 runner = Thread.currentThread();
 208 
 209                 // go for the run
 210                 return (Collection<? extends Result>) method.invoke(instances.get(), control, threadParams);
 211             } catch (Throwable e) {
 212                 // about to fail the iteration;
 213                 // compensate for missed sync-iteration latches, we don't care about that anymore
 214                 control.preSetupForce();
 215                 control.preTearDownForce();
 216 
 217                 if (control.benchmarkParams.shouldSynchIterations()) {
 218                     try {
 219                         control.announceWarmupReady();
 220                     } catch (Exception e1) {
 221                         // more threads than expected
 222                     }
 223 
 224                     try {
 225                         control.announceWarmdownReady();
 226                     } catch (Exception e1) {
 227                         // more threads than expected
 228                     }
 229                 }
 230 
 231                 throw new Exception(e); // wrapping Throwable
 232             } finally {
 233                 // unbind the executor thread
 234                 runner = null;
 235             }
 236         }
 237 
 238     }
 239 
 240 }