/* * Copyright (c) 2012, 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. * * 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. */ /* * @test * @bug 7050528 * @summary Set of micro-benchmarks testing throughput of java.text.DecimalFormat.format() * @author Olivier Lagneau * @run main FormatMicroBenchmark */ /* This is a set of micro-benchmarks testing throughput of java.text.DecimalFormat.format(). * It never fails. * * Usage and arguments: * - Run with no argument skips the whole benchmark and exits. * - Run with "-help" as first argument calls the usage() method and exits. * - Run with "-doit" runs the benchmark with summary details. * - Run with "-verbose" provides additional details on the run. * * Example run : * java -Xms500m -Xmx500m -XX:NewSize=400m FormatMicroBenchmark -doit -verbose * * Running with jtreg: * The jtreg header "run" tag options+args must be changed to avoid skipping * the execution. here is an example of run options: * "main/othervm -Xms500m -Xmx500m -XX:NewSize=400m FormatMicroBenchmark -doit" * * Note: * - Vm options -Xms, -Xmx, -XX:NewSize must be set correctly for * getting reliable numbers. Otherwise GC activity may corrupt results. * As of jdk80b48 using "-Xms500m -Xmx500m -XX:NewSize=400m" covers * all cases. * - Optionally using "-Xlog:gc" option provides information that * helps checking any GC activity while benches are run. * * Vm Options: * - Vm options to use (as of jdk80b48): * fast-path case : -Xms128m -Xmx128m -XX:NewSize=100m * non fast-path case: -Xms500m -Xmx500m -XX:NewSize=400m * or use worst case (non fast-path above) with both types of algorithm. * * - use -Xlog:gc to verify memory consumption of the benchmarks. * (See "Checking Memory Consumption" below). * * Description: * * Fast-path algorithm for format(double...) call stack is very different of * the standard call stack. Where the standard algorithm for formating double * uses internal class sun.misc.FloatingDecimal and its dtoa(double) method to * provide digits, fast-path embeds its own algorithm for binary to decimal * string conversion. * * FloatingDecimal always converts completely the passed double to a string. * Fast-path converts only to the needed digits since it follows constraints * on both the pattern rule, the DecimalFormat instance properties, and the * passed double. * * Micro benchmarks below measure the throughput for formating double values * using NumberFormat.format(double) call stack. The standard DecimalFormat * call stack as well as the fast-path algorithm implementation are sensitive * to the nature of the passed double values regarding throughput performance. * * These benchmarks are useful both for measuring the global performance gain * of fast-path and to check that any modification done on fast-path algorithm * does not bring any regression in the performance boost of fast-path. * * Note that these benchmarks will provide numbers without any knowledge of * the implementation of DecimalFormat class. So to check regression any run * should be compared to another reference run with a previous JDK, wether or * not this previous reference JDK contains fast-path implementation. * * The eight benchmarks below are dedicated to measure throughput on different * kinds of double that all fall in the fast-path case (all in Integer range): * * - Integer case : used double values are all "integer-like" (ex: -12345.0). * This is the benchFormatInteger micro-benchmark. * * - Fractional case : double values are "fractional" (ex: -0.12345). * This is the benchFormatFractional micro-benchmark. * * - Small integral case : like Integer case but double values are all limited * in their magnitude, from -500.0 to 500.0 if the number of iterations N is * set to 500000. * This is the benchFormatSmallIntegral micro-benchmark. * * - Fractional All Nines : doubles values have fractional part that is very * close to "999" (decimal pattern), or "99" (currency pattern), * or "0000...". * This is the benchFormatFractionalAllNines micro-benchmark. * * - All Nines : double values are such that both integral and fractional * part consist only of '9' digits. None of these values are rounded up. * This is the benchFormatAllNines micro-benchmark. * * - Fair simple case : calling J the loop variable and iterating over * the N number of iterations, used double values are computed as * d = (double) J + J*seed * where seed is a very small value that adds a fractional part and adds a * small number to integral part. Provides fairly distributed double values. * This is the benchFormatFairSimple micro-benchmark. * * - Fair case : this is a combination of small integral case and fair simple * case. Double values are limited in their magnitude but follow a parabolic * curve y = x**2 / K, keeping large magnitude only for large values of J. * The intent is trying to reproduce a distribution of double values as could * be found in a business application, with most values in either the low * range or the high range. * This is the benchFormatFair micro-benchmark. * * - Tie cases: values are very close to a tie case (iii...ii.fff5) * That is the worst situation that can happen for Fast-path algorithm when * considering throughput. * This is the benchFormatTie micro-benchmark. * * For all of the micro-benchmarks, the throughput load of the eventual * additional computations inside the loop is calculated prior to running the * benchmark, and provided in the output. That may be useful since this load * may vary for each architecture or machine configuration. * * The "-verbose" flag, when set, provides the throughput load numbers, the * time spent for each run of a benchmark, as well as an estimation of the * memory consumed by the runs. Beware of incremental GCs, see "Checking * Memory Consumption" section below. Every run should be done with correct * ms, mx, and NewSize vm options to get fully reliable numbers. * * The output provides the mean time needed for a benchmark after the server * jit compiler has done its optimization work if any. Thus only the last but * first three runs are taken into account in the time measurement (server jit * compiler shows to have done full optimization in most cases after the * second run, given a base number of iterations set to 500000). * * The program cleans up memory (stabilizeMemory() method) between each run of * the benchmarks to make sure that no garbage collection activity happens in * measurements. However that does not preclude incremental GCs activity that * may happen during the micro-benchmark if -Xms, -Xmx, and NewSize options * have not been tuned and set correctly. * * Checking Memory Consumption: * * For getting confidence in the throughput numbers, there must not give any * GC activity during the benchmark runs. That means that specific VM options * related to memory must be tuned for any given implementation of the JDK. * * Running with "-verbose" arguments will provide clues of the memory consumed * but is not enough, since any unexpected incremental GC may lower * artificially the estimation of the memory consumption. * * Options to set are -Xms, -Xmx, -XX:NewSize, plus -Xlog:gc to evaluate * correctly the values of these options. When running "-verbose", varying * numbers reported for memory consumption may indicate bad choices for these * options. * * For jdk80b25, fast-path shows a consuption of ~60Mbs for 500000 iterations * while a jdk without fast-path will consume ~260Mbs for each benchmark run. * Indeed these values will vary depending on the jdk used. * * Correct option settings found jdk80b48 were : * fast-path : -Xms128m -Xmx128m -XX:NewSize=100m * non fast-path : -Xms500m -Xmx500m -XX:NewSize=400m * Greater values can be provided safely but not smaller ones. * ---------------------------------------------------------------------- */ import java.util.*; import java.text.NumberFormat; import java.text.DecimalFormat; public class FormatMicroBenchmark { // The number of times the bench method will be run (must be at least 4). private static final int NB_RUNS = 20; // The bench* methods below all iterates over [-MAX_RANGE , +MAX_RANGE] integer values. private static final int MAX_RANGE = 500000; // Flag for more details on each bench run (default is no). private static boolean Verbose = false; // Should we really execute the benches ? (no by default). private static boolean DoIt = false; // Prints out a message describing how to run the program. private static void usage() { System.out.println( "This is a set of micro-benchmarks testing throughput of " + "java.text.DecimalFormat.format(). It never fails.\n\n" + "Usage and arguments:\n" + " - Run with no argument skips the whole benchmark and exits.\n" + " - Run with \"-help\" as first argument prints this message and exits.\n" + " - Run with \"-doit\" runs the benchmark with summary details.\n" + " - Run with \"-verbose\" provides additional details on the run.\n\n" + "Example run :\n" + " java -Xms500m -Xmx500m -XX:NewSize=400m FormatMicroBenchmark -doit -verbose\n\n" + "Note: \n" + " - Vm options -Xms, -Xmx, -XX:NewSize must be set correctly for \n" + " getting reliable numbers. Otherwise GC activity may corrupt results.\n" + " As of jdk80b48 using \"-Xms500m -Xmx500m -XX:NewSize=400m\" covers \n" + " all cases.\n" + " - Optionally using \"-Xlog:gc\" option provides information that \n" + " helps checking any GC activity while benches are run.\n\n" + "Look at the heading comments and description in source code for " + "detailed information.\n"); } /* We will call stabilizeMemory before each call of benchFormat***(). * This in turn tries to clean up as much memory as possible. * As a safe bound we limit number of System.gc() calls to 10, * but most of the time two calls to System.gc() will be enough. * If memory reporting is asked for, the method returns the difference * of free memory between entering an leaving the method. */ private static long stabilizeMemory(boolean reportConsumedMemory) { final long oneMegabyte = 1024L * 1024L; long refMemory = 0; long initialMemoryLeft = Runtime.getRuntime().freeMemory(); long currMemoryLeft = initialMemoryLeft; int nbGCCalls = 0; do { nbGCCalls++; refMemory = currMemoryLeft; System.gc(); currMemoryLeft = Runtime.getRuntime().freeMemory(); } while ((Math.abs(currMemoryLeft - refMemory) > oneMegabyte) && (nbGCCalls < 10)); if (Verbose && reportConsumedMemory) System.out.println("Memory consumed by previous run : " + (currMemoryLeft - initialMemoryLeft)/oneMegabyte + "Mbs."); return currMemoryLeft; } // ---------- Integer only based bench -------------------- private static final String INTEGER_BENCH = "benchFormatInteger"; private static String benchFormatInteger(NumberFormat nf) { String str = ""; for (int j = - MAX_RANGE; j <= MAX_RANGE; j++) str = nf.format((double) j); return str; } // This reproduces the throughput load added in benchFormatInteger static double integerThroughputLoad() { double d = 0.0d; for (int j = - MAX_RANGE; j <= MAX_RANGE; j++) { d = (double) j; } return d; } // Runs integerThroughputLoad and calculate its mean load static void calculateIntegerThroughputLoad() { int nbRuns = NB_RUNS; long elapsedTime = 0; double foo; for (int i = 1; i <= nbRuns; i++) { long startTime = System.nanoTime(); foo = integerThroughputLoad(); long estimatedTime = System.nanoTime() - startTime; if (i > 3) elapsedTime += estimatedTime / 1000; } if (Verbose) System.out.println( "calculated throughput load for " + INTEGER_BENCH + " bench is = " + (elapsedTime / (nbRuns - 3)) + " microseconds"); } // ---------- Fractional only based bench -------------------- private static final String FRACTIONAL_BENCH = "benchFormatFractional"; private static String benchFormatFractional(NumberFormat nf) { String str = ""; double floatingN = 1.0d / (double) MAX_RANGE; for (int j = - MAX_RANGE; j <= MAX_RANGE; j++) str = nf.format(floatingN * (double) j); return str; } // This reproduces the throughput load added in benchFormatFractional static double fractionalThroughputLoad() { double d = 0.0d; double floatingN = 1.0d / (double) MAX_RANGE; for (int j = - MAX_RANGE; j <= MAX_RANGE; j++) { d = floatingN * (double) j; } return d; } // Runs fractionalThroughputLoad and calculate its mean load static void calculateFractionalThroughputLoad() { int nbRuns = NB_RUNS; long elapsedTime = 0; double foo; for (int i = 1; i <= nbRuns; i++) { long startTime = System.nanoTime(); foo = fractionalThroughputLoad(); long estimatedTime = System.nanoTime() - startTime; if (i > 3) elapsedTime += estimatedTime / 1000; } if (Verbose) System.out.println( "calculated throughput load for " + FRACTIONAL_BENCH + " bench is = " + (elapsedTime / (nbRuns - 3)) + " microseconds"); } // ---------- An Small Integral bench -------------------- // that limits the magnitude of tested double values private static final String SMALL_INTEGRAL_BENCH = "benchFormatSmallIntegral"; private static String benchFormatSmallIntegral(NumberFormat nf) { String str = ""; for (int j = - MAX_RANGE; j <= MAX_RANGE; j++) str = nf.format(((double) j) / 1000.0d); return str; } // This reproduces the throughput load added in benchFormatSmallIntegral static double smallIntegralThroughputLoad() { double d = 0.0d; for (int j = - MAX_RANGE; j <= MAX_RANGE; j++) { d = (double) j / 1000.0d; } return d; } // Runs small_integralThroughputLoad and calculate its mean load static void calculateSmallIntegralThroughputLoad() { int nbRuns = NB_RUNS; long elapsedTime = 0; double foo; for (int i = 1; i <= nbRuns; i++) { long startTime = System.nanoTime(); foo = smallIntegralThroughputLoad(); long estimatedTime = System.nanoTime() - startTime; if (i > 3) elapsedTime += estimatedTime / 1000; } if (Verbose) System.out.println( "calculated throughput load for " + SMALL_INTEGRAL_BENCH + " bench is = " + (elapsedTime / (nbRuns - 3)) + " microseconds"); } // ---------- A fair and simple bench -------------------- private static final String FAIR_SIMPLE_BENCH = "benchFormatFairSimple"; private static String benchFormatFairSimple(NumberFormat nf, boolean isCurrency) { String str = ""; double seed = isCurrency ? 0.0010203040506070809 : 0.00010203040506070809; double d = (double) -MAX_RANGE; for (int j = - MAX_RANGE; j <= MAX_RANGE; j++) { d = d + 1.0d + seed; str = nf.format(d); } return str; } // This reproduces the throughput load added in benchFormatFairSimple static double fairSimpleThroughputLoad() { double seed = 0.00010203040506070809; double delta = 0.0d; double d = (double) -MAX_RANGE; for (int j = - MAX_RANGE; j <= MAX_RANGE; j++) { d = d + 1.0d + seed; } return d; } // Runs fairThroughputLoad and calculate its mean load static void calculateFairSimpleThroughputLoad() { int nbRuns = NB_RUNS; long elapsedTime = 0; double foo; for (int i = 1; i <= nbRuns; i++) { long startTime = System.nanoTime(); foo = fairSimpleThroughputLoad(); long estimatedTime = System.nanoTime() - startTime; if (i > 3) elapsedTime += estimatedTime / 1000; } if (Verbose) System.out.println( "calculated throughput load for " + FAIR_SIMPLE_BENCH + " bench is = " + (elapsedTime / (nbRuns - 3)) + " microseconds"); } // ---------- Fractional part is only made of nines bench -------------- private static final String FRACTIONAL_ALL_NINES_BENCH = "benchFormatFractionalAllNines"; private static String benchFormatFractionalAllNines(NumberFormat nf, boolean isCurrency) { String str = ""; double fractionalEven = isCurrency ? 0.993000001 : 0.99930000001; double fractionalOdd = isCurrency ? 0.996000001 : 0.99960000001; double fractional; double d; for (int j = - MAX_RANGE; j <= MAX_RANGE; j++) { if ((j & 1) == 0) fractional = fractionalEven; else fractional = fractionalOdd; if ( j >= 0) d = (double ) j + fractional; else d = (double) j - fractional; str = nf.format(d); } return str; } // This reproduces the throughput load added in benchFormatFractionalAllNines static double fractionalAllNinesThroughputLoad() { double fractionalEven = 0.99930000001; double fractionalOdd = 0.99960000001; double fractional; double d = 0.0d; for (int j = - MAX_RANGE; j <= MAX_RANGE; j++) { if ((j & 1) == 0) fractional = fractionalEven; else fractional = fractionalOdd; if ( j >= 0) d = (double ) j + fractional; else d = (double) j - fractional; } return d; } // Runs fractionalAllNinesThroughputLoad and calculate its mean load static void calculateFractionalAllNinesThroughputLoad() { int nbRuns = NB_RUNS; long elapsedTime = 0; double foo; for (int i = 1; i <= nbRuns; i++) { long startTime = System.nanoTime(); foo = fractionalAllNinesThroughputLoad(); long estimatedTime = System.nanoTime() - startTime; if (i > 3) elapsedTime += estimatedTime / 1000; } if (Verbose) System.out.println( "calculated throughput load for " + FRACTIONAL_ALL_NINES_BENCH + " bench is = " + (elapsedTime / (nbRuns - 3)) + " microseconds"); } // ---------- Number is only made of nines bench -------------- private static final String ALL_NINES_BENCH = "benchFormatAllNines"; private static String benchFormatAllNines(NumberFormat nf, boolean isCurrency) { String str = ""; double[] decimaAllNines = {9.9993, 99.9993, 999.9993, 9999.9993, 99999.9993, 999999.9993, 9999999.9993, 99999999.9993, 999999999.9993}; double[] currencyAllNines = {9.993, 99.993, 999.993, 9999.993, 99999.993, 999999.993, 9999999.993, 99999999.993, 999999999.993}; double[] valuesArray = (isCurrency) ? currencyAllNines : decimaAllNines; double seed = 1.0 / (double) MAX_RANGE; double d; int id; for (int j = - MAX_RANGE; j <= MAX_RANGE; j++) { id = (j >= 0) ? j % 9 : -j % 9; if ((j & 1) == 0) d = valuesArray[id] + id * seed; else d = valuesArray[id] - id * seed; str = nf.format(d); } return str; } // This reproduces the throughput load added in benchFormatAllNines static double allNinesThroughputLoad() { double[] decimaAllNines = {9.9993, 99.9993, 999.9993, 9999.9993, 99999.9993, 999999.9993, 9999999.9993, 99999999.9993, 999999999.9993}; double[] valuesArray = decimaAllNines; double seed = 1.0 / (double) MAX_RANGE; double d = 0.0d; int id; for (int j = - MAX_RANGE; j <= MAX_RANGE; j++) { id = (j >= 0) ? j % 9 : -j % 9; if ((j & 1) == 0) d = valuesArray[id] + id * seed; else d = valuesArray[id] - id * seed; } return d; } // Runs allNinesThroughputLoad and calculate its mean load static void calculateAllNinesThroughputLoad() { int nbRuns = NB_RUNS; long elapsedTime = 0; double foo; for (int i = 1; i <= nbRuns; i++) { long startTime = System.nanoTime(); foo = allNinesThroughputLoad(); long estimatedTime = System.nanoTime() - startTime; if (i > 3) elapsedTime += estimatedTime / 1000; } if (Verbose) System.out.println( "calculated throughput load for " + ALL_NINES_BENCH + " bench is = " + (elapsedTime / (nbRuns - 3)) + " microseconds"); } // --- A fair bench trying (hopefully) to reproduce business applicatons --- /* benchFormatFair uses the following formula : * y = F(x) = sign(x) * x**2 * ((1000/MAX_RANGE)**2). * * which converts in the loop as (if j is the loop index) : * x = double(j) * k = 1000.0d * double(MAX_RANGE) * y = sign(j) * x**2 * k**2 * * This is a flattened parabolic curve where only the j values * in [-1000, 1000] will provide y results in [-1, +1] interval, * and for abs(j) >= 1000 the result y will be greater than 1. * * The difference with benchFormatSmallIntegral is that since y results * follow a parabolic curve the magnitude of y grows much more rapidly * and closer to j values when abs(j) >= 1000: * - for |j| < 1000, SmallIntegral(j) < 1.0 and fair(j) < 1.0 * - for j in [1000, 10000[ * SmallIntegral(j) is in [1, 10[ * Fair(j) is in [4, 400[ * - for j in [10000,100000[ * SmallIntegral(j) is in [10, 100[ * Fair(j) is in [400,40000[ * - for j in [100000,1000000[ * SmallIntegral(j) is in [100, 1000[ * Fair(j) is in [40000, 4000000[ * * Since double values for j less than 100000 provide only 4 digits in the * integral, values greater than 250000 provide at least 6 digits, and 500000 * computes to 1000000, the distribution is roughly half with less than 5 * digits and half with at least 6 digits in the integral part. * * Compared to FairSimple bench, this represents an application where 20% of * the double values to format are less than 40000.0 absolute value. * * Fair(j) is close to the magnitude of j when j > 100000 and is hopefully * more representative of what may be found in general in business apps. * (assumption : there will be mainly either small or large values, and * less values in middle range). * * We could get even more precise distribution of values using formula : * y = sign(x) * abs(x)**n * ((1000 / MAX_RANGE)**n) where n > 2, * or even well-known statistics function to fine target such distribution, * but we have considred that the throughput load for calculating y would * then be too high. We thus restrain the use of a power of 2 formula. */ private static final String FAIR_BENCH = "benchFormatFair"; private static String benchFormatFair(NumberFormat nf) { String str = ""; double k = 1000.0d / (double) MAX_RANGE; k *= k; double d; double absj; double jPowerOf2; for (int j = - MAX_RANGE; j <= MAX_RANGE; j++) { absj = (double) j; jPowerOf2 = absj * absj; d = k * jPowerOf2; if (j < 0) d = -d; str = nf.format(d); } return str; } // This is the exact throughput load added in benchFormatFair static double fairThroughputLoad() { double k = 1000.0d / (double) MAX_RANGE; k *= k; double d = 0.0d; double absj; double jPowerOf2; for (int j = - MAX_RANGE; j <= MAX_RANGE; j++) { absj = (double) j; jPowerOf2 = absj * absj; d = k * jPowerOf2; if (j < 0) d = -d; } return d; } // Runs fairThroughputLoad and calculate its mean load static void calculateFairThroughputLoad() { int nbRuns = NB_RUNS; long elapsedTime = 0; double foo; for (int i = 1; i <= nbRuns; i++) { long startTime = System.nanoTime(); foo = fairThroughputLoad(); long estimatedTime = System.nanoTime() - startTime; if (i > 3) elapsedTime += estimatedTime / 1000; } if (Verbose) System.out.println( "calculated throughput load for " + FAIR_BENCH + " bench is = " + (elapsedTime / (nbRuns - 3)) + " microseconds"); } // ---------- All double values are very close to a tie -------------------- // i.e. like 123.1235 (for decimal case) or 123.125 (for currency case). private static final String TIE_BENCH = "benchFormatTie"; private static String benchFormatTie(NumberFormat nf, boolean isCurrency) { double d; String str = ""; double fractionaScaling = (isCurrency) ? 1000.0d : 10000.0d; int fixedFractionalPart = (isCurrency) ? 125 : 1235; for (int j = - MAX_RANGE; j <= MAX_RANGE; j++) { d = (((double) j * fractionaScaling) + (double) fixedFractionalPart) / fractionaScaling; str = nf.format(d); } return str; } // This is the exact throughput load added in benchFormatTie static double tieThroughputLoad(boolean isCurrency) { double d = 0.0d; double fractionaScaling = (isCurrency) ? 1000.0d : 10000.0d; int fixedFractionalPart = (isCurrency) ? 125 : 1235; for (int j = - MAX_RANGE; j <= MAX_RANGE; j++) { d = (((double) j * fractionaScaling) + (double) fixedFractionalPart) / fractionaScaling; } return d; } // Runs tieThroughputLoad and calculate its mean load static void calculateTieThroughputLoad(boolean isCurrency) { int nbRuns = NB_RUNS; long elapsedTime = 0; double foo; for (int i = 1; i <= nbRuns; i++) { long startTime = System.nanoTime(); foo = tieThroughputLoad(isCurrency); long estimatedTime = System.nanoTime() - startTime; if (i > 3) elapsedTime += estimatedTime / 1000; } if (Verbose) System.out.println( "calculated throughput load for " + TIE_BENCH + " bench is = " + (elapsedTime / (nbRuns - 3)) + " microseconds"); } // Print statistics for passed times results of benchName. static void printPerfResults(long[] times, String benchName) { int nbBenches = times.length; long totalTimeSpent = 0; long meanTimeSpent; double variance = 0; double standardDeviation = 0; // Calculates mean spent time for (int i = 1; i <= nbBenches; i++) totalTimeSpent += times[i-1]; meanTimeSpent = totalTimeSpent / nbBenches; // Calculates standard deviation for (int j = 1; j <= nbBenches; j++) variance += Math.pow(((double)times[j-1] - (double)meanTimeSpent), 2); variance = variance / (double) times.length; standardDeviation = Math.sqrt(variance) / meanTimeSpent; // Print result and statistics for benchName System.out.println( "Statistics (starting at 4th bench) for bench " + benchName + "\n for last " + nbBenches + " runs out of " + NB_RUNS + " , each with 2x" + MAX_RANGE + " format(double) calls : " + "\n mean exec time = " + meanTimeSpent + " microseconds" + "\n standard deviation = " + String.format("%.3f", standardDeviation) + "% \n"); } public static void main(String[] args) { if (args.length >= 1) { // Parse args, just checks expected ones. Ignore others or dups. if (args[0].equals("-help")) { usage(); return; } for (String s : args) { if (s.equals("-doit")) DoIt = true; else if (s.equals("-verbose")) Verbose = true; } } else { // No arguments, skips the benchmarks and exits. System.out.println( "Test skipped with success by default. See -help for details."); return; } if (!DoIt) { if (Verbose) usage(); System.out.println( "Test skipped and considered successful."); return; } System.out.println("Single Threaded micro benchmark evaluating " + "the throughput of java.text.DecimalFormat.format() call stack.\n"); String fooString = ""; // Run benches for decimal instance DecimalFormat df = (DecimalFormat) NumberFormat.getInstance(Locale.US); System.out.println("Running with a decimal instance of DecimalFormat."); calculateIntegerThroughputLoad(); fooString = BenchType.INTEGER_BENCH.runBenchAndPrintStatistics(NB_RUNS, df, false); calculateFractionalThroughputLoad(); fooString = BenchType.FRACTIONAL_BENCH.runBenchAndPrintStatistics(NB_RUNS, df, false); calculateSmallIntegralThroughputLoad(); fooString = BenchType.SMALL_INTEGRAL_BENCH.runBenchAndPrintStatistics(NB_RUNS, df, false); calculateFractionalAllNinesThroughputLoad(); fooString = BenchType.FRACTIONAL_ALL_NINES_BENCH.runBenchAndPrintStatistics(NB_RUNS, df, false); calculateAllNinesThroughputLoad(); fooString = BenchType.ALL_NINES_BENCH.runBenchAndPrintStatistics(NB_RUNS, df, false); calculateFairSimpleThroughputLoad(); fooString = BenchType.FAIR_SIMPLE_BENCH.runBenchAndPrintStatistics(NB_RUNS, df, false); calculateFairThroughputLoad(); fooString = BenchType.FAIR_BENCH.runBenchAndPrintStatistics(NB_RUNS, df, false); calculateTieThroughputLoad(false); fooString = BenchType.TIE_BENCH.runBenchAndPrintStatistics(NB_RUNS, df, false); // Run benches for currency instance DecimalFormat cf = (DecimalFormat) NumberFormat.getCurrencyInstance(Locale.US); System.out.println("Running with a currency instance of DecimalFormat."); calculateIntegerThroughputLoad(); fooString = BenchType.INTEGER_BENCH.runBenchAndPrintStatistics(NB_RUNS, cf, false); calculateFractionalThroughputLoad(); fooString = BenchType.FRACTIONAL_BENCH.runBenchAndPrintStatistics(NB_RUNS, cf, false); calculateSmallIntegralThroughputLoad(); fooString = BenchType.SMALL_INTEGRAL_BENCH.runBenchAndPrintStatistics(NB_RUNS, cf, false); calculateFractionalAllNinesThroughputLoad(); fooString = BenchType.FRACTIONAL_ALL_NINES_BENCH.runBenchAndPrintStatistics(NB_RUNS, cf, false); calculateAllNinesThroughputLoad(); fooString = BenchType.ALL_NINES_BENCH.runBenchAndPrintStatistics(NB_RUNS, cf, false); calculateFairSimpleThroughputLoad(); fooString = BenchType.FAIR_SIMPLE_BENCH.runBenchAndPrintStatistics(NB_RUNS, cf, false); calculateFairThroughputLoad(); fooString = BenchType.FAIR_BENCH.runBenchAndPrintStatistics(NB_RUNS, cf, false); calculateTieThroughputLoad(false); fooString = BenchType.TIE_BENCH.runBenchAndPrintStatistics(NB_RUNS, cf, false); } // This class to factorise what would be duplicated otherwise. static enum BenchType { INTEGER_BENCH("benchFormatInteger"), FRACTIONAL_BENCH("benchFormatFractional"), SMALL_INTEGRAL_BENCH("benchFormatSmallIntegral"), FAIR_SIMPLE_BENCH("benchFormatFairSimple"), FRACTIONAL_ALL_NINES_BENCH("benchFormatFractionalAllNines"), ALL_NINES_BENCH("benchFormatAllNines"), FAIR_BENCH("benchFormatFair"), TIE_BENCH("benchFormatTie"); private final String name; BenchType(String name) { this.name = name; } String runBenchAndPrintStatistics(int nbRuns, NumberFormat nf, boolean isCurrency) { // We eliminate the first 3 runs in the time measurements // to let C2 do complete compilation and optimization work. long[] elapsedTimes = new long[nbRuns - 3]; System.out.println("Now running " + nbRuns + " times bench " + name); String str = ""; for (int i = 1; i <= nbRuns; i++) { stabilizeMemory(false); long startTime = System.nanoTime(); switch(this) { case INTEGER_BENCH : str = benchFormatInteger(nf); break; case FRACTIONAL_BENCH : str = benchFormatFractional(nf); break; case SMALL_INTEGRAL_BENCH : str = benchFormatSmallIntegral(nf); break; case FRACTIONAL_ALL_NINES_BENCH : str = benchFormatFractionalAllNines(nf, isCurrency); break; case ALL_NINES_BENCH : str = benchFormatAllNines(nf, isCurrency); break; case FAIR_SIMPLE_BENCH : str = benchFormatFairSimple(nf, isCurrency); break; case FAIR_BENCH : str = benchFormatFair(nf); break; case TIE_BENCH : str = benchFormatTie(nf, isCurrency); break; default: } long estimatedTime = System.nanoTime() - startTime; if (i > 3) elapsedTimes[i-4] = estimatedTime / 1000; if (Verbose) System.out.println( "calculated time for " + name + " bench " + i + " is = " + (estimatedTime / 1000) + " microseconds"); else System.out.print("."); stabilizeMemory(true); } System.out.println(name + " Done."); printPerfResults(elapsedTimes, name); return str; } } }