| /* |
| * 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; |
| } |
| } |
| |
| } |