changeset 1176:bab2376915b4

7901365: Selectable score precision and treating near-zero results
author shade
date Wed, 01 Apr 2015 19:56:40 +0300
parents f9c1d1965462
children 3d5bb9a657a5
files jmh-core/src/main/java/org/openjdk/jmh/profile/LinuxPerfNormProfiler.java jmh-core/src/main/java/org/openjdk/jmh/profile/LinuxPerfProfiler.java jmh-core/src/main/java/org/openjdk/jmh/profile/ProfilerResult.java jmh-core/src/main/java/org/openjdk/jmh/results/Result.java jmh-core/src/main/java/org/openjdk/jmh/results/format/TextResultFormat.java jmh-core/src/main/java/org/openjdk/jmh/util/ScoreFormatter.java
diffstat 6 files changed, 127 insertions(+), 33 deletions(-) [+]
line wrap: on
line diff
--- a/jmh-core/src/main/java/org/openjdk/jmh/profile/LinuxPerfNormProfiler.java	Wed Apr 01 18:11:18 2015 +0300
+++ b/jmh-core/src/main/java/org/openjdk/jmh/profile/LinuxPerfNormProfiler.java	Wed Apr 01 19:56:40 2015 +0300
@@ -36,6 +36,7 @@
 import org.openjdk.jmh.util.HashMultiset;
 import org.openjdk.jmh.util.ListStatistics;
 import org.openjdk.jmh.util.Multiset;
+import org.openjdk.jmh.util.ScoreFormatter;
 import org.openjdk.jmh.util.Statistics;
 import org.openjdk.jmh.util.Utils;
 
@@ -322,7 +323,7 @@
 
         @Override
         public String toString() {
-            return String.format(" %.3f %s/op", getScore(), key);
+            return String.format(" %s %s/op", ScoreFormatter.format(getScore()), key);
         }
 
         @Override
--- a/jmh-core/src/main/java/org/openjdk/jmh/profile/LinuxPerfProfiler.java	Wed Apr 01 18:11:18 2015 +0300
+++ b/jmh-core/src/main/java/org/openjdk/jmh/profile/LinuxPerfProfiler.java	Wed Apr 01 19:56:40 2015 +0300
@@ -31,6 +31,7 @@
 import org.openjdk.jmh.results.Result;
 import org.openjdk.jmh.results.ResultRole;
 import org.openjdk.jmh.util.FileUtils;
+import org.openjdk.jmh.util.ScoreFormatter;
 import org.openjdk.jmh.util.Utils;
 
 import java.io.BufferedReader;
@@ -219,7 +220,7 @@
 
         @Override
         public String toString() {
-            return String.format("%.3f cycles per instruction", 1.0 * cycles / instructions);
+            return String.format("%s cycles per instruction", ScoreFormatter.format(1.0 * cycles / instructions));
         }
 
         @Override
--- a/jmh-core/src/main/java/org/openjdk/jmh/profile/ProfilerResult.java	Wed Apr 01 18:11:18 2015 +0300
+++ b/jmh-core/src/main/java/org/openjdk/jmh/profile/ProfilerResult.java	Wed Apr 01 19:56:40 2015 +0300
@@ -28,6 +28,7 @@
 import org.openjdk.jmh.results.Aggregator;
 import org.openjdk.jmh.results.Result;
 import org.openjdk.jmh.results.ResultRole;
+import org.openjdk.jmh.util.ScoreFormatter;
 import org.openjdk.jmh.util.Statistics;
 
 public class ProfilerResult extends Result<ProfilerResult> {
@@ -54,10 +55,10 @@
     public String extendedInfo(String label) {
         switch (policy) {
             case AVG:
-                return String.format("Result %30s: %.3f ±(99.9%%) %.3f %s", "\"" + label + "\"", getScore(), getScoreError(), getScoreUnit());
+                return String.format("Result %30s: %s ±(99.9%%) %s %s", "\"" + label + "\"", ScoreFormatter.format(getScore()), ScoreFormatter.format(getScoreError()), getScoreUnit());
             case MAX:
             case SUM:
-                return String.format("Result %30s: %.3f %s [%s]", "\"" + label + "\"", getScore(), getScoreUnit(), policy);
+                return String.format("Result %30s: %s %s [%s]", "\"" + label + "\"", ScoreFormatter.format(getScore()), getScoreUnit(), policy);
             default:
                 throw new IllegalStateException("Unknown policy: " + policy);
         }
--- a/jmh-core/src/main/java/org/openjdk/jmh/results/Result.java	Wed Apr 01 18:11:18 2015 +0300
+++ b/jmh-core/src/main/java/org/openjdk/jmh/results/Result.java	Wed Apr 01 19:56:40 2015 +0300
@@ -25,6 +25,7 @@
 package org.openjdk.jmh.results;
 
 import org.openjdk.jmh.util.Deduplicator;
+import org.openjdk.jmh.util.ScoreFormatter;
 import org.openjdk.jmh.util.SingletonStatistics;
 import org.openjdk.jmh.util.Statistics;
 
@@ -176,10 +177,15 @@
      */
     @Override
     public String toString() {
-        if (!Double.isNaN(getScoreError())) {
-            return String.format("%.3f \u00B1(99.9%%) %.3f %s", getScore(), getScoreError(), getScoreUnit());
+        if (!Double.isNaN(getScoreError()) && !ScoreFormatter.isApproximate(getScore())) {
+            return String.format("%s \u00B1(99.9%%) %sf %s",
+                    ScoreFormatter.format(getScore()),
+                    ScoreFormatter.formatError(getScoreError()),
+                    getScoreUnit());
         } else {
-            return String.format("%.3f %s", getScore(), getScoreUnit());
+            return String.format("%s %s",
+                    ScoreFormatter.format(getScore()),
+                    getScoreUnit());
         }
     }
 
@@ -197,18 +203,24 @@
         PrintWriter pw = new PrintWriter(sw);
 
         Statistics stats = getStatistics();
-        if (stats.getN() > 2) {
+        if (stats.getN() > 2 && !ScoreFormatter.isApproximate(getScore())) {
             double[] interval = getScoreConfidence();
-            pw.println(String.format("Result%s: %.3f \u00B1(99.9%%) %.3f %s [%s]",
+            pw.println(String.format("Result%s: %s \u00B1(99.9%%) %s %s [%s]",
                     (label == null) ? "" : " \"" + label + "\"",
-                    getScore(), (interval[1] - interval[0]) / 2,
+                    ScoreFormatter.format(getScore()),
+                    ScoreFormatter.formatError((interval[1] - interval[0]) / 2),
                     getScoreUnit(), policy));
-            pw.println(String.format("  Statistics: (min, avg, max) = (%.3f, %.3f, %.3f), stdev = %.3f%n" +
-                    "  Confidence interval (99.9%%): [%.3f, %.3f]",
-                    stats.getMin(), stats.getMean(), stats.getMax(), stats.getStandardDeviation(),
-                    interval[0], interval[1]));
+            pw.println(String.format("  Statistics: (min, avg, max) = (%s, %s, %s), stdev = %s%n" +
+                    "  Confidence interval (99.9%%): [%s, %s]",
+                    ScoreFormatter.format(stats.getMin()),
+                    ScoreFormatter.format(stats.getMean()),
+                    ScoreFormatter.format(stats.getMax()),
+                    ScoreFormatter.formatError(stats.getStandardDeviation()),
+                    ScoreFormatter.format(interval[0]),
+                    ScoreFormatter.format(interval[1]))
+            );
         } else {
-            pw.println(String.format("Run result: %.2f %s (<= 2 samples)", stats.getMean(), getScoreUnit()));
+            pw.println(String.format("Run result: %s %s", ScoreFormatter.format(stats.getMean()), getScoreUnit()));
         }
         pw.close();
         return sw.toString();
@@ -223,23 +235,25 @@
             sb.append("  Samples, N = ").append(stats.getN()).append("\n");
 
             double[] interval = stats.getConfidenceIntervalAt(0.999);
-            sb.append(String.format("        mean = %10.3f \u00B1(99.9%%) %.3f",
-                    stats.getMean(),
-                    (interval[1] - interval[0]) / 2
+            sb.append(String.format("        mean = %s \u00B1(99.9%%) %s",
+                    ScoreFormatter.format(10, stats.getMean()),
+                    ScoreFormatter.formatError((interval[1] - interval[0]) / 2)
             ));
             sb.append(" ").append(getScoreUnit()).append("\n");
 
-            sb.append(String.format("         min = %10.3f %s\n", stats.getMin(), getScoreUnit()));
+            sb.append(String.format("         min = %s %s\n", ScoreFormatter.format(stats.getMin()), getScoreUnit()));
 
             for (double p : new double[]{0.00, 0.50, 0.90, 0.95, 0.99, 0.999, 0.9999, 0.99999, 0.999999}) {
-                sb.append(String.format("  %9s = %10.3f %s\n",
+                sb.append(String.format("  %9s = %s %s\n",
                         "p(" + String.format("%7.4f", p * 100) + ")",
-                        stats.getPercentile(p * 100),
+                        ScoreFormatter.format(10, stats.getPercentile(p * 100)),
                         getScoreUnit()
-                ));
+                        ));
             }
 
-            sb.append(String.format("         max = %10.3f %s\n", stats.getMax(), getScoreUnit()));
+            sb.append(String.format("         max = %s %s\n",
+                    ScoreFormatter.format(10, stats.getMax()),
+                    getScoreUnit()));
         }
 
         return sb.toString();
--- a/jmh-core/src/main/java/org/openjdk/jmh/results/format/TextResultFormat.java	Wed Apr 01 18:11:18 2015 +0300
+++ b/jmh-core/src/main/java/org/openjdk/jmh/results/format/TextResultFormat.java	Wed Apr 01 19:56:40 2015 +0300
@@ -28,6 +28,7 @@
 import org.openjdk.jmh.results.Result;
 import org.openjdk.jmh.results.RunResult;
 import org.openjdk.jmh.util.ClassUtils;
+import org.openjdk.jmh.util.ScoreFormatter;
 
 import java.io.PrintStream;
 import java.util.ArrayList;
@@ -91,14 +92,14 @@
 
             modeLen     = Math.max(modeLen,     res.getParams().getMode().shortLabel().length());
             samplesLen  = Math.max(samplesLen,  String.format("%d",   primRes.getSampleCount()).length());
-            scoreLen    = Math.max(scoreLen,    String.format("%.3f", primRes.getScore()).length());
-            scoreErrLen = Math.max(scoreErrLen, String.format("%.3f", primRes.getScoreError()).length());
+            scoreLen    = Math.max(scoreLen,    ScoreFormatter.format(primRes.getScore()).length());
+            scoreErrLen = Math.max(scoreErrLen, ScoreFormatter.format(primRes.getScoreError()).length());
             unitLen     = Math.max(unitLen,     primRes.getScoreUnit().length());
 
             for (Result subRes : res.getSecondaryResults().values()) {
                 samplesLen  = Math.max(samplesLen,  String.format("%d",   subRes.getSampleCount()).length());
-                scoreLen    = Math.max(scoreLen,    String.format("%.3f", subRes.getScore()).length());
-                scoreErrLen = Math.max(scoreErrLen, String.format("%.3f", subRes.getScoreError()).length());
+                scoreLen    = Math.max(scoreLen,    ScoreFormatter.format(subRes.getScore()).length());
+                scoreErrLen = Math.max(scoreErrLen, ScoreFormatter.format(subRes.getScoreError()).length());
                 unitLen     = Math.max(unitLen,     subRes.getScoreUnit().length());
             }
         }
@@ -139,11 +140,11 @@
                     out.printf("%" + samplesLen + "s", "");
                 }
 
-                out.printf("%" + scoreLen + ".3f", pRes.getScore());
+                out.print(ScoreFormatter.format(scoreLen, pRes.getScore()));
 
-                if (!Double.isNaN(pRes.getScoreError())) {
+                if (!Double.isNaN(pRes.getScoreError()) && !ScoreFormatter.isApproximate(pRes.getScore())) {
                     out.print(" \u00B1");
-                    out.printf("%" + scoreErrLen + ".3f", pRes.getScoreError());
+                    out.print(ScoreFormatter.formatError(scoreErrLen, pRes.getScoreError()));
                 } else {
                     out.print("  ");
                     out.printf("%" + scoreErrLen + "s", "");
@@ -173,11 +174,11 @@
                     out.printf("%" + samplesLen + "s", "");
                 }
 
-                out.printf("%" + scoreLen + ".3f", subRes.getScore());
+                out.print(ScoreFormatter.format(scoreLen, subRes.getScore()));
 
-                if (!Double.isNaN(subRes.getScoreError())) {
+                if (!Double.isNaN(subRes.getScoreError()) && !ScoreFormatter.isApproximate(subRes.getScore())) {
                     out.print(" \u00B1");
-                    out.printf("%" + scoreErrLen + ".3f", subRes.getScoreError());
+                    out.print(ScoreFormatter.formatError(scoreErrLen, subRes.getScoreError()));
                 } else {
                     out.print("  ");
                     out.printf("%" + scoreErrLen + "s", "");
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jmh-core/src/main/java/org/openjdk/jmh/util/ScoreFormatter.java	Wed Apr 01 19:56:40 2015 +0300
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2014, 2015, 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.util;
+
+public class ScoreFormatter {
+
+    private static final int PRECISION = Integer.getInteger("jmh.scorePrecision", 3);
+    private static final double ULP = 1.0 / Math.pow(10, PRECISION);
+    private static final double THRESHOLD = ULP / 2;
+
+    public static boolean isApproximate(double score) {
+        return (score < THRESHOLD);
+    }
+
+    public static String format(double score) {
+        if (isApproximate(score)) {
+            return "\u2248 10" + superscript("" + (int)Math.round(Math.log10(score)));
+        } else {
+            return String.format("%." + PRECISION + "f", score);
+        }
+    }
+
+    public static String format(int width, double score) {
+        if (isApproximate(score)) {
+            return String.format("%" + width + "s", "\u2248 10" + superscript("" + (int)Math.round(Math.log10(score))));
+        } else {
+            return String.format("%" + width + "." + PRECISION + "f", score);
+        }
+    }
+
+    public static String formatError(double error) {
+        return String.format("%." + PRECISION + "f", Math.max(error, ULP));
+    }
+
+    public static String formatError(int width, double error) {
+        return String.format("%" + width + "." + PRECISION + "f", Math.max(error, ULP));
+    }
+
+    public static String superscript(String str) {
+        str = str.replaceAll("-", "\u207b");
+        str = str.replaceAll("0", "\u2070");
+        str = str.replaceAll("1", "\u00b9");
+        str = str.replaceAll("2", "\u00b2");
+        str = str.replaceAll("3", "\u00b3");
+        str = str.replaceAll("4", "\u2074");
+        str = str.replaceAll("5", "\u2075");
+        str = str.replaceAll("6", "\u2076");
+        str = str.replaceAll("7", "\u2077");
+        str = str.replaceAll("8", "\u2078");
+        str = str.replaceAll("9", "\u2079");
+        return str;
+    }
+
+}