/*
 * Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */
package org.openjdk.jmh.runner.format;

import org.openjdk.jmh.annotations.Mode;
import org.openjdk.jmh.infra.BenchmarkParams;
import org.openjdk.jmh.infra.IterationParams;
import org.openjdk.jmh.results.BenchmarkResult;
import org.openjdk.jmh.results.IterationResult;
import org.openjdk.jmh.results.Result;
import org.openjdk.jmh.results.RunResult;
import org.openjdk.jmh.results.format.ResultFormatFactory;
import org.openjdk.jmh.results.format.ResultFormatType;
import org.openjdk.jmh.runner.CompilerHints;
import org.openjdk.jmh.runner.IterationType;
import org.openjdk.jmh.runner.options.TimeValue;
import org.openjdk.jmh.runner.options.VerboseMode;
import org.openjdk.jmh.util.Utils;

import java.io.PrintStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

TextReportFormat implementation of OutputFormat.
/** * TextReportFormat implementation of OutputFormat. */
class TextReportFormat extends AbstractOutputFormat { public TextReportFormat(PrintStream out, VerboseMode verbose) { super(out, verbose); } @Override public void startBenchmark(BenchmarkParams params) { String opts = Utils.join(params.getJvmArgs(), " "); if (opts.trim().isEmpty()) { opts = "<none>"; } println("# JMH version: " + params.getJmhVersion()); println("# VM version: JDK " + params.getJdkVersion() + ", " + params.getVmName() + ", " + params.getVmVersion()); switch (SupportedVMs.supportLevel(params.getVmName())) { case FULL: break; case EXPERIMENTAL: println("# *** WARNING: JMH support for this VM is experimental. Be extra careful with the produced data."); break; case NONE: println("# *** WARNING: This VM is not supported by JMH. The produced benchmark data can be completely wrong."); break; default: throw new IllegalStateException("Unknown support level"); } println("# VM invoker: " + params.getJvm()); println("# VM options: " + opts); CompilerHints.printBlackholeMode(out); IterationParams warmup = params.getWarmup(); if (warmup.getCount() > 0) { out.println("# Warmup: " + warmup.getCount() + " iterations, " + warmup.getTime() + " each" + (warmup.getBatchSize() <= 1 ? "" : ", " + warmup.getBatchSize() + " calls per op")); } else { out.println("# Warmup: <none>"); } IterationParams measurement = params.getMeasurement(); if (measurement.getCount() > 0) { out.println("# Measurement: " + measurement.getCount() + " iterations, " + measurement.getTime() + " each" + (measurement.getBatchSize() <= 1 ? "" : ", " + measurement.getBatchSize() + " calls per op")); } else { out.println("# Measurement: <none>"); } TimeValue timeout = params.getTimeout(); boolean timeoutWarning = (timeout.convertTo(TimeUnit.NANOSECONDS) <= measurement.getTime().convertTo(TimeUnit.NANOSECONDS)) || (timeout.convertTo(TimeUnit.NANOSECONDS) <= warmup.getTime().convertTo(TimeUnit.NANOSECONDS)); out.println("# Timeout: " + timeout + " per iteration" + (timeoutWarning ? ", ***WARNING: The timeout might be too low!***" : "")); out.print("# Threads: " + params.getThreads() + " " + getThreadsString(params.getThreads())); if (!params.getThreadGroupLabels().isEmpty()) { int[] tg = params.getThreadGroups(); // TODO: Make params.getThreadGroupLabels return List List<String> labels = new ArrayList<>(params.getThreadGroupLabels()); String[] ss = new String[tg.length]; for (int cnt = 0; cnt < tg.length; cnt++) { ss[cnt] = tg[cnt] + "x \"" + labels.get(cnt) + "\""; } int groupCount = params.getThreads() / Utils.sum(tg); out.print(" (" + groupCount + " " + getGroupsString(groupCount) + "; " + Utils.join(ss, ", ") + " in each group)"); } out.println(params.shouldSynchIterations() ? ", will synchronize iterations" : (params.getMode() == Mode.SingleShotTime) ? "" : ", ***WARNING: Synchronize iterations are disabled!***"); out.println("# Benchmark mode: " + params.getMode().longLabel()); out.println("# Benchmark: " + params.getBenchmark()); if (!params.getParamsKeys().isEmpty()) { String s = ""; boolean isFirst = true; for (String k : params.getParamsKeys()) { if (isFirst) { isFirst = false; } else { s += ", "; } s += k + " = " + params.getParam(k); } out.println("# Parameters: (" + s + ")"); } } @Override public void iteration(BenchmarkParams benchmarkParams, IterationParams params, int iteration) { switch (params.getType()) { case WARMUP: out.print(String.format("# Warmup Iteration %3d: ", iteration)); break; case MEASUREMENT: out.print(String.format("Iteration %3d: ", iteration)); break; default: throw new IllegalStateException("Unknown iteration type: " + params.getType()); } out.flush(); } protected static String getThreadsString(int t) { if (t > 1) { return "threads"; } else { return "thread"; } } protected static String getGroupsString(int g) { if (g > 1) { return "groups"; } else { return "group"; } } @Override public void iterationResult(BenchmarkParams benchmParams, IterationParams params, int iteration, IterationResult data) { StringBuilder sb = new StringBuilder(); sb.append(data.getPrimaryResult().toString()); if (params.getType() == IterationType.MEASUREMENT) { int prefixLen = String.format("Iteration %3d: ", iteration).length(); Map<String, Result> secondary = data.getSecondaryResults(); if (!secondary.isEmpty()) { sb.append("\n"); int maxKeyLen = 0; for (Map.Entry<String, Result> res : secondary.entrySet()) { maxKeyLen = Math.max(maxKeyLen, res.getKey().length()); } for (Map.Entry<String, Result> res : secondary.entrySet()) { sb.append(String.format("%" + prefixLen + "s", "")); sb.append(String.format(" %-" + (maxKeyLen + 1) + "s %s", res.getKey() + ":", res.getValue())); sb.append("\n"); } } } out.print(String.format("%s%n", sb.toString())); out.flush(); } @Override public void endBenchmark(BenchmarkResult result) { out.println(); if (result != null) { { Result r = result.getPrimaryResult(); String s = r.extendedInfo(); if (!s.trim().isEmpty()) { out.println("Result \"" + result.getParams().getBenchmark() + "\":"); out.println(s); } } for (Result r : result.getSecondaryResults().values()) { String s = r.extendedInfo(); if (!s.trim().isEmpty()) { out.println("Secondary result \"" + result.getParams().getBenchmark() + ":" + r.getLabel() + "\":"); out.println(s); } } out.println(); } } @Override public void startRun() { // do nothing } @Override public void endRun(Collection<RunResult> runResults) { out.println("REMEMBER: The numbers below are just data. To gain reusable insights, you need to follow up on"); out.println("why the numbers are the way they are. Use profilers (see -prof, -lprof), design factorial"); out.println("experiments, perform baseline and negative tests that provide experimental control, make sure"); out.println("the benchmarking environment is safe on JVM/OS/HW level, ask for reviews from the domain experts."); out.println("Do not assume the numbers tell you what you want them to tell."); out.println(); ResultFormatFactory.getInstance(ResultFormatType.TEXT, out).writeOut(runResults); } }