org.openjdk.jmh.results.format.JSONResultFormat Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of virtdata-lib-curves4 Show documentation
Show all versions of virtdata-lib-curves4 Show documentation
Statistical sampling library for use in virtdata libraries, based
on apache commons math 4
/*
* 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.results.format;
import org.openjdk.jmh.infra.BenchmarkParams;
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.util.Statistics;
import org.openjdk.jmh.util.Utils;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Map;
class JSONResultFormat implements ResultFormat {
private static final boolean PRINT_RAW_DATA =
Boolean.parseBoolean(System.getProperty("jmh.json.rawData", "true"));
private final PrintStream out;
public JSONResultFormat(PrintStream out) {
this.out = out;
}
@Override
public void writeOut(Collection results) {
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
boolean first = true;
pw.println("[");
for (RunResult runResult : results) {
BenchmarkParams params = runResult.getParams();
if (first) {
first = false;
pw.println();
} else {
pw.println(",");
}
pw.println("{");
pw.println("\"jmhVersion\" : \"" + params.getJmhVersion() + "\",");
pw.println("\"benchmark\" : \"" + params.getBenchmark() + "\",");
pw.println("\"mode\" : \"" + params.getMode().shortLabel() + "\",");
pw.println("\"threads\" : " + params.getThreads() + ",");
pw.println("\"forks\" : " + params.getForks() + ",");
pw.println("\"jvm\" : " + toJsonString(params.getJvm()) + ",");
// if empty, write an empty array.
pw.println("\"jvmArgs\" : [");
printStringArray(pw, params.getJvmArgs());
pw.println("],");
pw.println("\"jdkVersion\" : " + toJsonString(params.getJdkVersion()) + ",");
pw.println("\"vmName\" : " + toJsonString(params.getVmName()) + ",");
pw.println("\"vmVersion\" : " + toJsonString(params.getVmVersion()) + ",");
pw.println("\"warmupIterations\" : " + params.getWarmup().getCount() + ",");
pw.println("\"warmupTime\" : \"" + params.getWarmup().getTime() + "\",");
pw.println("\"warmupBatchSize\" : " + params.getWarmup().getBatchSize() + ",");
pw.println("\"measurementIterations\" : " + params.getMeasurement().getCount() + ",");
pw.println("\"measurementTime\" : \"" + params.getMeasurement().getTime() + "\",");
pw.println("\"measurementBatchSize\" : " + params.getMeasurement().getBatchSize() + ",");
if (!params.getParamsKeys().isEmpty()) {
pw.println("\"params\" : {");
pw.println(emitParams(params));
pw.println("},");
}
Result primaryResult = runResult.getPrimaryResult();
pw.println("\"primaryMetric\" : {");
pw.println("\"score\" : " + emit(primaryResult.getScore()) + ",");
pw.println("\"scoreError\" : " + emit(primaryResult.getScoreError()) + ",");
pw.println("\"scoreConfidence\" : " + emit(primaryResult.getScoreConfidence()) + ",");
pw.println(emitPercentiles(primaryResult.getStatistics()));
pw.println("\"scoreUnit\" : \"" + primaryResult.getScoreUnit() + "\",");
switch (params.getMode()) {
case SampleTime:
pw.println("\"rawDataHistogram\" :");
pw.println(getRawData(runResult, true));
break;
default:
pw.println("\"rawData\" :");
pw.println(getRawData(runResult, false));
}
pw.println("},"); // primaryMetric end
Collection secondaries = new ArrayList<>();
for (Map.Entry e : runResult.getSecondaryResults().entrySet()) {
String secondaryName = e.getKey();
Result result = e.getValue();
StringBuilder sb = new StringBuilder();
sb.append("\"").append(secondaryName).append("\" : {");
sb.append("\"score\" : ").append(emit(result.getScore())).append(",");
sb.append("\"scoreError\" : ").append(emit(result.getScoreError())).append(",");
sb.append("\"scoreConfidence\" : ").append(emit(result.getScoreConfidence())).append(",");
sb.append(emitPercentiles(result.getStatistics()));
sb.append("\"scoreUnit\" : \"").append(result.getScoreUnit()).append("\",");
sb.append("\"rawData\" : ");
Collection l2 = new ArrayList<>();
for (BenchmarkResult benchmarkResult : runResult.getBenchmarkResults()) {
Collection scores = new ArrayList<>();
for (IterationResult r : benchmarkResult.getIterationResults()) {
Result rr = r.getSecondaryResults().get(secondaryName);
if (rr != null) {
scores.add(emit(rr.getScore()));
}
}
l2.add(printMultiple(scores, "[", "]"));
}
sb.append(printMultiple(l2, "[", "]"));
sb.append("}");
secondaries.add(sb.toString());
}
pw.println("\"secondaryMetrics\" : {");
pw.println(printMultiple(secondaries, "", ""));
pw.println("}");
pw.print("}"); // benchmark end
}
pw.println("]");
out.println(tidy(sw.toString()));
}
private String getRawData(RunResult runResult, boolean histogram) {
StringBuilder sb = new StringBuilder();
Collection runs = new ArrayList<>();
if (PRINT_RAW_DATA) {
for (BenchmarkResult benchmarkResult : runResult.getBenchmarkResults()) {
Collection iterations = new ArrayList<>();
for (IterationResult r : benchmarkResult.getIterationResults()) {
if (histogram) {
Collection singleIter = new ArrayList<>();
for (Map.Entry item : Utils.adaptForLoop(r.getPrimaryResult().getStatistics().getRawData())) {
singleIter.add("< " + emit(item.getKey()) + "; " + item.getValue() + " >");
}
iterations.add(printMultiple(singleIter, "[", "]"));
} else {
iterations.add(emit(r.getPrimaryResult().getScore()));
}
}
runs.add(printMultiple(iterations, "[", "]"));
}
}
sb.append(printMultiple(runs, "[", "]"));
return sb.toString();
}
private String emitParams(BenchmarkParams params) {
StringBuilder sb = new StringBuilder();
boolean isFirst = true;
for (String k : params.getParamsKeys()) {
if (isFirst) {
isFirst = false;
} else {
sb.append(", ");
}
sb.append("\"").append(k).append("\" : ");
sb.append(toJsonString(params.getParam(k)));
}
return sb.toString();
}
private String emitPercentiles(Statistics stats) {
StringBuilder sb = new StringBuilder();
sb.append("\"scorePercentiles\" : {");
boolean firstPercentile = true;
for (double p : new double[]{0.00, 50.0, 90, 95, 99, 99.9, 99.99, 99.999, 99.9999, 100}) {
if (firstPercentile) {
firstPercentile = false;
} else {
sb.append(",");
}
double v = stats.getPercentile(p);
sb.append("\"").append(emit(p)).append("\" : ");
sb.append(emit(v));
}
sb.append("},");
return sb.toString();
}
private String emit(double[] ds) {
StringBuilder sb = new StringBuilder();
boolean isFirst = true;
sb.append("[");
for (double d : ds) {
if (isFirst) {
isFirst = false;
} else {
sb.append(",");
}
sb.append(emit(d));
}
sb.append("]");
return sb.toString();
}
private String emit(double d) {
if (d != d)
return "\"NaN\"";
if (d == Double.NEGATIVE_INFINITY)
return "\"-INF\"";
if (d == Double.POSITIVE_INFINITY)
return "\"+INF\"";
return String.valueOf(d);
}
/**
* Escaping for a JSON string. Does the typical escaping of double quotes and backslash.
* Also escapes characters that are handled by the tidying process, so that every ASCII
* character makes it correctly into the JSON output. Control characters are filtered.
*/
static String toJsonString(String s) {
StringBuilder sb = new StringBuilder();
sb.append("\"");
for (char c : s.toCharArray()) {
if (Character.isISOControl(c)) {
continue;
}
switch (c) {
// use & as escape character to escape the tidying
case '&': sb.append("&&"); break;
// we cannot escape to \\\\ since this would create sequences interpreted by the tidying
case '\\': sb.append("&/"); break;
case '"': sb.append("&'"); break;
// escape spacial chars for the tidying formatting below that might appear in a string
case ',': sb.append(";"); break;
case '[': sb.append("<"); break;
case ']': sb.append(">"); break;
case '<': sb.append("&-"); break;
case '>': sb.append("&="); break;
case ';': sb.append("&:"); break;
case '{': sb.append("&("); break;
case '}': sb.append("&)"); break;
default: sb.append(c);
}
}
sb.append("\"");
return sb.toString();
}
static String tidy(String s) {
s = s.replaceAll("\r", "");
s = s.replaceAll("\n", " ");
s = s.replaceAll(",", ",\n");
s = s.replaceAll("\\{", "{\n");
s = s.replaceAll("\\[", "[\n");
s = s.replaceAll("\\}", "\n}\n");
s = s.replaceAll("\\]", "\n]\n");
s = s.replaceAll("\\]\n,\n", "],\n");
s = s.replaceAll("\\}\n,\n", "},\n");
s = s.replaceAll("\n( *)\n", "\n");
// Keep these inline:
s = s.replaceAll(";", ",");
s = s.replaceAll("\\<", "[");
s = s.replaceAll("\\>", "]");
// translate back from string escaping to keep all string characters intact
s = s.replaceAll("&:", ";");
s = s.replaceAll("&'", "\\\\\"");
s = s.replaceAll("&\\(", "{");
s = s.replaceAll("&\\)", "}");
s = s.replaceAll("&-", "<");
s = s.replaceAll("&=", ">");
s = s.replaceAll("&/", "\\\\\\\\");
s = s.replaceAll("&&", "&");
String[] lines = s.split("\n");
StringBuilder sb = new StringBuilder();
int ident = 0;
String prevL = null;
for (String l : lines) {
if (prevL != null && (prevL.endsWith("{") || prevL.endsWith("["))) {
ident++;
}
if (l.equals("}") || l.equals("]") || l.equals("},") || l.equals("],")) {
ident--;
}
for (int c = 0; c < ident; c++) {
sb.append(" ");
}
sb.append(l.trim());
sb.append("\n");
prevL = l;
}
return sb.toString();
}
private String printMultiple(Collection elements, String leftBracket, String rightBracket) {
StringBuilder sb = new StringBuilder();
sb.append(leftBracket);
boolean isFirst = true;
for (String e : elements) {
if (isFirst) {
isFirst = false;
} else {
sb.append(",");
}
sb.append(e);
}
sb.append(rightBracket);
return sb.toString();
}
private static void printStringArray(PrintWriter pw, Collection col) {
boolean isFirst = true;
for (String e : col) {
if (isFirst) {
isFirst = false;
} else {
pw.print(',');
}
pw.print(toJsonString(e));
}
}
}