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 }