/* * Copyright 2013 MovingBlocks * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.terasology.benchmark; import com.google.common.base.Preconditions; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.concurrent.TimeUnit; /** * Benchmarks contains methods to execute one or many benchmarks with support for a progress callback as well as * a simple pretty printer for benchmark results. * */ public final class Benchmarks { private Benchmarks() { } public static BenchmarkResult execute(Benchmark benchmark, int benchmarkIndex, int benchmarkCount, BenchmarkCallback callback) { if (callback != null) { callback.begin(benchmark, benchmarkIndex, benchmarkCount); } final BenchmarkResult result = new BasicBenchmarkResult(benchmark); final int[] repetitions = Preconditions.checkNotNull(benchmark.getRepetitions(), "Benchmark::getRepetitions() must not return null"); Preconditions.checkState(repetitions.length > 0, "Benchmark::getRepetitions() must return an array of size greater than zero"); try { benchmark.setup(); } catch (Exception e) { result.addError(BenchmarkError.Type.Setup, e); if (callback != null) { callback.error(BenchmarkError.Type.Setup, e, result); } return result; } try { if (callback != null) { callback.warmup(benchmark, false); } for (int i = 0; i < benchmark.getWarmupRepetitions(); ++i) { benchmark.run(); } if (callback != null) { callback.warmup(benchmark, true); } } catch (Exception e) { result.addError(BenchmarkError.Type.Warmup, e); if (callback != null) { callback.error(BenchmarkError.Type.Warmup, e, result); } return result; } int repsTotal = 0; int repsSoFar = 0; for (int reps : repetitions) { repsTotal += reps; } int repsPart = repsTotal / 20; int repIndex = 0; boolean aborted = false; for (int reps : repetitions) { try { benchmark.prerun(); } catch (Exception e) { aborted = true; result.addError(BenchmarkError.Type.PreRun, e); if (callback != null) { callback.error(BenchmarkError.Type.PreRun, e, result); } break; } long start = time(); long elapsed; try { result.setStartTime(repIndex, TimeUnit.MILLISECONDS.convert(start, TimeUnit.NANOSECONDS)); int currentReps = reps; while (currentReps > repsPart) { for (int i = 0; i < repsPart; ++i) { benchmark.run(); } currentReps -= repsPart; repsSoFar += repsPart; if (callback != null) { callback.progress(benchmark, 100d / (double) repsTotal * (double) repsSoFar); } } if (currentReps <= repsPart) { for (int i = 0; i < currentReps; ++i) { benchmark.run(); } repsSoFar += currentReps; if (callback != null) { callback.progress(benchmark, 100d / (double) repsTotal * (double) repsSoFar); } } elapsed = elapsed(start, TimeUnit.NANOSECONDS); result.setRunTime(repIndex, TimeUnit.MILLISECONDS.convert(elapsed, TimeUnit.NANOSECONDS)); } catch (Exception e) { aborted = true; result.addError(BenchmarkError.Type.Run, e); if (callback != null) { callback.error(BenchmarkError.Type.Run, e, result); } break; } try { benchmark.postrun(); } catch (Exception e) { aborted = true; result.addError(BenchmarkError.Type.PostRun, e); if (callback != null) { callback.error(BenchmarkError.Type.PostRun, e, result); } break; } ++repIndex; } try { benchmark.finish(aborted); } catch (Exception e) { result.addError(BenchmarkError.Type.Finish, e); if (callback != null) { callback.error(BenchmarkError.Type.Finish, e, result); } } if (callback != null) { if (result.isAborted()) { callback.aborted(result); } else { callback.success(result); } } return result; } public static List<BenchmarkResult> execute(List<Benchmark> benchmarks, BenchmarkCallback callback) { Preconditions.checkNotNull(benchmarks); final List<BenchmarkResult> results = new LinkedList<>(); final int benchmarkCount = benchmarks.size(); try { int benchmarkIndex = 1; for (Benchmark benchmark : benchmarks) { Preconditions.checkNotNull(benchmark); BenchmarkResult result = execute(benchmark, benchmarkIndex, benchmarkCount, callback); results.add(result); benchmarkIndex++; } } catch (Exception e) { if (callback != null) { callback.fatal(e); } } return results; } public static StringBuilder printResults(List<BenchmarkResult> results) { return printResults(results, new StringBuilder()); } public static StringBuilder printResults(List<BenchmarkResult> results, StringBuilder b) { Preconditions.checkNotNull(b); final int resultCount = results.size(); int resultIndex = 1; for (BenchmarkResult result : results) { if (resultIndex > 1) { b.append("\n"); } b.append("Benchmark " + resultIndex + " / " + resultCount + ": " + result.getTitle() + "\n"); printResult(result, b); resultIndex++; } return b; } public static StringBuilder printResult(BenchmarkResult result) { return printResult(result, new StringBuilder()); } public static StringBuilder printResult(BenchmarkResult result, StringBuilder b) { Preconditions.checkNotNull(b); BenchmarkResult.Column<?>[] columns = getColumns(result); printFieldTitles(result, columns, b); for (int repIndex = 0; repIndex < result.getRepetitions(); repIndex++) { printFieldValues(result, repIndex, columns, b); } return b; } private static BenchmarkResult.Column<?>[] getColumns(BenchmarkResult result) { BenchmarkResult.Column<?>[] columns = new BenchmarkResult.Column<?>[result.getNumColumns()]; Iterator<BenchmarkResult.Column<?>> it = result.getColumnsIterator(); int i = 0; while (it.hasNext()) { columns[i++] = it.next(); } return columns; } private static void printFieldTitles(BenchmarkResult result, BenchmarkResult.Column<?>[] columns, StringBuilder b) { boolean first = true; for (BenchmarkResult.Column<?> col : columns) { if (first) { first = false; } else { b.append(" | "); } b.append(col.alignment.pad(col.name, col.computeMaxWidth())); } b.append("\n"); } private static void printFieldValues(BenchmarkResult result, int repIndex, BenchmarkResult.Column<?>[] columns, StringBuilder b) { boolean first = true; for (BenchmarkResult.Column<?> col : columns) { if (first) { first = false; } else { b.append(" | "); } b.append(col.alignment.pad(col.getValue(repIndex), col.computeMaxWidth())); } b.append("\n"); } private static long time() { return System.nanoTime(); } private static long elapsed(long time, TimeUnit unit) { Preconditions.checkNotNull(unit); long result = time() - time; return unit.convert(result, TimeUnit.NANOSECONDS); } }