1 /*
   2  * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 
  24 /*
  25  * @test
  26  * @bug 7050528
  27  * @summary Set of micro-benchmarks testing throughput of java.text.DecimalFormat.format()
  28  * @author Olivier Lagneau
  29  * @run main FormatMicroBenchmark
  30  */
  31 
  32 /* This is a set of micro-benchmarks testing throughput of java.text.DecimalFormat.format().
  33  * It never fails.
  34  *
  35  * Usage and arguments:
  36  *  - Run with no argument skips the whole benchmark and exits.
  37  *  - Run with "-help" as first argument calls the usage() method and exits.
  38  *  - Run with "-doit" runs the benchmark with summary details.
  39  *  - Run with "-verbose" provides additional details on the run.
  40  *
  41  * Example run :
  42  *   java -Xms500m -Xmx500m -XX:NewSize=400m FormatMicroBenchmark -doit -verbose
  43  *
  44  * Running with jtreg:
  45  *  The jtreg header "run" tag options+args must be changed to avoid skipping
  46  *  the execution. here is an example of run options:
  47  *  "main/othervm -Xms500m -Xmx500m -XX:NewSize=400m FormatMicroBenchmark -doit"
  48  *
  49  * Note:
  50  *  - Vm options -Xms, -Xmx, -XX:NewSize must be set correctly for
  51  *    getting reliable numbers. Otherwise GC activity may corrupt results.
  52  *    As of jdk80b48 using "-Xms500m -Xmx500m -XX:NewSize=400m" covers
  53  *    all cases.
  54  *  - Optionally using "-Xlog:gc" option provides information that
  55  *    helps checking any GC activity while benches are run.
  56  *
  57  * Vm Options:
  58  *  - Vm options to use (as of jdk80b48):
  59  *     fast-path case :     -Xms128m -Xmx128m -XX:NewSize=100m
  60  *     non fast-path case:  -Xms500m -Xmx500m -XX:NewSize=400m
  61  *    or use worst case (non fast-path above) with both types of algorithm.
  62  *
  63  *  - use -Xlog:gc to verify memory consumption of the benchmarks.
  64  *    (See "Checking Memory Consumption" below).
  65  *
  66  * Description:
  67  *
  68  *  Fast-path algorithm for format(double...)  call stack is very different  of
  69  *  the standard call stack. Where the  standard algorithm for formating double
  70  *  uses internal class sun.misc.FloatingDecimal and its dtoa(double) method to
  71  *  provide digits,  fast-path embeds its own  algorithm for  binary to decimal
  72  *  string conversion.
  73  *
  74  *  FloatingDecimal always converts completely  the passed double to  a string.
  75  *  Fast-path converts  only to the needed digits  since it follows constraints
  76  *  on both the pattern rule,  the  DecimalFormat instance properties, and  the
  77  *  passed double.
  78  *
  79  *  Micro benchmarks below measure  the throughput for formating double  values
  80  *  using NumberFormat.format(double)  call stack.  The  standard DecimalFormat
  81  *  call stack as well as the  fast-path algorithm implementation are sensitive
  82  *  to the nature of the passed double values regarding throughput performance.
  83  *
  84  *  These benchmarks are useful both  for measuring the global performance gain
  85  *  of fast-path and to check that any modification done on fast-path algorithm
  86  *  does not bring any regression in the performance boost of fast-path.
  87  *
  88  *  Note  that these benchmarks  will provide numbers  without any knowledge of
  89  *  the  implementation of DecimalFormat class. So  to check regression any run
  90  *  should be compared to another reference run with  a previous JDK, wether or
  91  *  not this previous reference JDK contains fast-path implementation.
  92  *
  93  *  The eight benchmarks below are dedicated to measure throughput on different
  94  *  kinds of double that all fall in the fast-path case (all in Integer range):
  95  *
  96  *  - Integer case : used double values are all "integer-like" (ex: -12345.0).
  97  *    This is the benchFormatInteger micro-benchmark.
  98  *
  99  *  - Fractional case : double values are "fractional" (ex: -0.12345).
 100  *    This is the benchFormatFractional micro-benchmark.
 101  *
 102  *  - Small integral case : like Integer case but double values are all limited
 103  *    in their magnitude, from -500.0 to 500.0 if the number of iterations N is
 104  *    set to 500000.
 105  *    This is the benchFormatSmallIntegral micro-benchmark.
 106  *
 107  *  - Fractional All Nines : doubles values have fractional part that is very
 108  *    close to "999" (decimal pattern), or "99" (currency pattern),
 109  *    or "0000...".
 110  *    This is the benchFormatFractionalAllNines micro-benchmark.
 111  *
 112  *  - All Nines : double values are such that both integral and fractional
 113  *    part consist only of '9' digits. None of these values are rounded up.
 114  *    This is the benchFormatAllNines micro-benchmark.
 115  *
 116  *  - Fair simple case : calling J the loop variable and iterating over
 117  *    the N number of iterations, used double values are computed as
 118  *    d = (double) J + J*seed
 119  *    where seed is a very small value that adds a fractional part and adds a
 120  *    small number to integral part. Provides fairly distributed double values.
 121  *    This is the benchFormatFairSimple micro-benchmark.
 122  *
 123  *  - Fair case : this is a combination of small integral case and fair simple
 124  *    case. Double values are limited in their magnitude but follow a parabolic
 125  *    curve y = x**2 / K, keeping large magnitude only for large values of J.
 126  *    The intent is trying to reproduce a distribution of double values as could
 127  *    be found in a business application, with most values in either the low
 128  *    range or the high range.
 129  *    This is the benchFormatFair micro-benchmark.
 130  *
 131  *  - Tie cases: values are very close to a tie case (iii...ii.fff5)
 132  *    That is the worst situation that can happen for Fast-path algorithm when
 133  *    considering throughput.
 134  *    This is the benchFormatTie micro-benchmark.
 135  *
 136  *  For  all  of  the micro-benchmarks,  the  throughput load   of the eventual
 137  *  additional computations inside the loop is calculated  prior to running the
 138  *  benchmark, and provided in the output.  That may be  useful since this load
 139  *  may vary for each architecture or machine configuration.
 140  *
 141  *  The "-verbose" flag,  when set, provides the  throughput  load numbers, the
 142  *  time spent for  each run of  a benchmark, as  well as an estimation  of the
 143  *  memory consumed  by the  runs.  Beware of  incremental  GCs, see  "Checking
 144  *  Memory  Consumption" section below. Every run   should be done with correct
 145  *  ms, mx, and NewSize vm options to get fully reliable numbers.
 146  *
 147  *  The output provides the  mean time needed for  a benchmark after the server
 148  *  jit compiler has done its optimization work if  any. Thus only the last but
 149  *  first three runs are taken into account in the time measurement (server jit
 150  *  compiler shows  to have  done full  optimization  in  most cases  after the
 151  *  second run, given a base number of iterations set to 500000).
 152  *
 153  *  The program cleans up memory (stabilizeMemory() method) between each run of
 154  *  the benchmarks to make sure that  no garbage collection activity happens in
 155  *  measurements. However that does not  preclude incremental GCs activity that
 156  *  may  happen during the micro-benchmark if  -Xms, -Xmx, and NewSize options
 157  *  have not been tuned and set correctly.
 158  *
 159  * Checking Memory Consumption:
 160  *
 161  *  For getting confidence  in the throughput numbers, there  must not give any
 162  *  GC activity during the benchmark runs. That  means that specific VM options
 163  *  related to memory must be tuned for any given implementation of the JDK.
 164  *
 165  *  Running with "-verbose" arguments will provide  clues of the memory consumed
 166  *  but  is   not enough,  since  any   unexpected  incremental  GC  may  lower
 167  *  artificially the estimation of the memory consumption.
 168  *
 169  *  Options to  set are -Xms, -Xmx,  -XX:NewSize, plus -Xlog:gc to evaluate
 170  *  correctly  the  values of  these options. When  running "-verbose", varying
 171  *  numbers reported for memory consumption may  indicate bad choices for these
 172  *  options.
 173  *
 174  *  For jdk80b25, fast-path shows a consuption of ~60Mbs for 500000 iterations
 175  *  while a jdk without fast-path will consume ~260Mbs for each benchmark run.
 176  *  Indeed these values will vary depending on the jdk used.
 177  *
 178  *  Correct option settings found jdk80b48 were :
 179  *     fast-path :     -Xms128m -Xmx128m -XX:NewSize=100m
 180  *     non fast-path : -Xms500m -Xmx500m -XX:NewSize=400m
 181  *  Greater values can be provided safely but not smaller ones.
 182  * ----------------------------------------------------------------------
 183  */
 184 
 185 import java.util.*;
 186 import java.text.NumberFormat;
 187 import java.text.DecimalFormat;
 188 
 189 public class FormatMicroBenchmark {
 190 
 191     // The number of times the bench method will be run (must be at least 4).
 192     private static final int NB_RUNS = 20;
 193 
 194     // The bench* methods below all iterates over [-MAX_RANGE , +MAX_RANGE] integer values.
 195     private static final int MAX_RANGE = 500000;
 196 
 197     // Flag for more details on each bench run (default is no).
 198     private static boolean Verbose = false;
 199 
 200     // Should we really execute the benches ? (no by default).
 201     private static boolean DoIt = false;
 202 
 203     // Prints out a message describing how to run the program.
 204     private static void usage() {
 205         System.out.println(
 206             "This is a set of micro-benchmarks testing throughput of " +
 207             "java.text.DecimalFormat.format(). It never fails.\n\n" +
 208             "Usage and arguments:\n" +
 209             " - Run with no argument skips the whole benchmark and exits.\n" +
 210             " - Run with \"-help\" as first argument prints this message and exits.\n" +
 211             " - Run with \"-doit\" runs the benchmark with summary details.\n" +
 212             " - Run with \"-verbose\" provides additional details on the run.\n\n" +
 213             "Example run :\n" +
 214             "   java -Xms500m -Xmx500m -XX:NewSize=400m FormatMicroBenchmark -doit -verbose\n\n" +
 215             "Note: \n" +
 216             " - Vm options -Xms, -Xmx, -XX:NewSize must be set correctly for \n" +
 217             "   getting reliable numbers. Otherwise GC activity may corrupt results.\n" +
 218             "   As of jdk80b48 using \"-Xms500m -Xmx500m -XX:NewSize=400m\" covers \n" +
 219             "   all cases.\n" +
 220             " - Optionally using \"-Xlog:gc\" option provides information that \n" +
 221             "   helps checking any GC activity while benches are run.\n\n" +
 222             "Look at the heading comments and description in source code for " +
 223             "detailed information.\n");
 224     }
 225 
 226     /* We will call stabilizeMemory before each call of benchFormat***().
 227      * This in turn tries to clean up as much memory as possible.
 228      * As a safe bound we limit number of System.gc() calls to 10,
 229      * but most of the time two calls to System.gc() will be enough.
 230      * If memory reporting is asked for, the method returns the difference
 231      * of free memory between entering an leaving the method.
 232      */
 233     private static long stabilizeMemory(boolean reportConsumedMemory) {
 234         final long oneMegabyte = 1024L * 1024L;
 235 
 236         long refMemory = 0;
 237         long initialMemoryLeft = Runtime.getRuntime().freeMemory();
 238         long currMemoryLeft = initialMemoryLeft;
 239         int nbGCCalls = 0;
 240 
 241         do {
 242             nbGCCalls++;
 243 
 244             refMemory = currMemoryLeft;
 245             System.gc();
 246             currMemoryLeft = Runtime.getRuntime().freeMemory();
 247 
 248         } while ((Math.abs(currMemoryLeft - refMemory) > oneMegabyte) &&
 249                  (nbGCCalls < 10));
 250 
 251         if (Verbose &&
 252             reportConsumedMemory)
 253             System.out.println("Memory consumed by previous run : " +
 254                                (currMemoryLeft - initialMemoryLeft)/oneMegabyte + "Mbs.");
 255 
 256         return currMemoryLeft;
 257     }
 258 
 259 
 260     // ---------- Integer only based bench --------------------
 261     private static final String INTEGER_BENCH = "benchFormatInteger";
 262     private static String benchFormatInteger(NumberFormat nf) {
 263         String str = "";
 264         for (int j = - MAX_RANGE; j <= MAX_RANGE; j++)
 265             str = nf.format((double) j);
 266         return str;
 267     }
 268 
 269     // This reproduces the throughput load added in benchFormatInteger
 270     static double integerThroughputLoad() {
 271         double d = 0.0d;
 272         for (int j = - MAX_RANGE; j <= MAX_RANGE; j++) {
 273             d = (double) j;
 274         }
 275         return d;
 276     }
 277 
 278     // Runs integerThroughputLoad and calculate its mean load
 279     static void calculateIntegerThroughputLoad() {
 280         int nbRuns = NB_RUNS;
 281         long elapsedTime = 0;
 282         double foo;
 283 
 284         for (int i = 1; i <= nbRuns; i++) {
 285 
 286             long startTime = System.nanoTime();
 287             foo = integerThroughputLoad();
 288             long estimatedTime = System.nanoTime() - startTime;
 289             if (i > 3) elapsedTime += estimatedTime / 1000;
 290         }
 291 
 292 
 293         if (Verbose)
 294             System.out.println(
 295                "calculated throughput load for " + INTEGER_BENCH +
 296                " bench is = " + (elapsedTime / (nbRuns - 3)) + " microseconds");
 297     }
 298 
 299     // ---------- Fractional only based bench --------------------
 300     private static final String FRACTIONAL_BENCH = "benchFormatFractional";
 301     private static String benchFormatFractional(NumberFormat nf) {
 302         String str = "";
 303         double floatingN = 1.0d / (double) MAX_RANGE;
 304         for (int j = - MAX_RANGE; j <= MAX_RANGE; j++)
 305             str = nf.format(floatingN * (double) j);
 306         return str;
 307     }
 308 
 309     // This reproduces the throughput load added in benchFormatFractional
 310     static double fractionalThroughputLoad() {
 311         double d = 0.0d;
 312         double floatingN = 1.0d / (double) MAX_RANGE;
 313         for (int j = - MAX_RANGE; j <= MAX_RANGE; j++) {
 314             d = floatingN * (double) j;
 315         }
 316         return d;
 317     }
 318 
 319     // Runs fractionalThroughputLoad and calculate its mean load
 320     static void calculateFractionalThroughputLoad() {
 321         int nbRuns = NB_RUNS;
 322         long elapsedTime = 0;
 323         double foo;
 324 
 325         for (int i = 1; i <= nbRuns; i++) {
 326 
 327             long startTime = System.nanoTime();
 328             foo = fractionalThroughputLoad();
 329             long estimatedTime = System.nanoTime() - startTime;
 330             if (i > 3) elapsedTime += estimatedTime / 1000;
 331         }
 332 
 333         if (Verbose)
 334         System.out.println(
 335             "calculated throughput load for " + FRACTIONAL_BENCH +
 336             " bench is = " + (elapsedTime / (nbRuns - 3)) + " microseconds");
 337     }
 338 
 339     // ---------- An Small Integral bench --------------------
 340     //  that limits the magnitude of tested double values
 341     private static final String SMALL_INTEGRAL_BENCH = "benchFormatSmallIntegral";
 342     private static String benchFormatSmallIntegral(NumberFormat nf) {
 343         String str = "";
 344         for (int j = - MAX_RANGE; j <= MAX_RANGE; j++)
 345             str = nf.format(((double) j) / 1000.0d);
 346         return str;
 347     }
 348 
 349     // This reproduces the throughput load added in benchFormatSmallIntegral
 350     static double smallIntegralThroughputLoad() {
 351         double d = 0.0d;
 352         for (int j = - MAX_RANGE; j <= MAX_RANGE; j++) {
 353             d = (double) j / 1000.0d;
 354         }
 355         return d;
 356     }
 357 
 358     // Runs small_integralThroughputLoad and calculate its mean load
 359     static void calculateSmallIntegralThroughputLoad() {
 360         int nbRuns = NB_RUNS;
 361         long elapsedTime = 0;
 362         double foo;
 363 
 364         for (int i = 1; i <= nbRuns; i++) {
 365 
 366             long startTime = System.nanoTime();
 367             foo = smallIntegralThroughputLoad();
 368             long estimatedTime = System.nanoTime() - startTime;
 369             if (i > 3) elapsedTime += estimatedTime / 1000;
 370         }
 371 
 372         if (Verbose)
 373         System.out.println(
 374             "calculated throughput load for " + SMALL_INTEGRAL_BENCH +
 375             " bench is = " + (elapsedTime / (nbRuns - 3)) + " microseconds");
 376     }
 377 
 378     // ---------- A fair and simple bench --------------------
 379     private static final String FAIR_SIMPLE_BENCH = "benchFormatFairSimple";
 380     private static String benchFormatFairSimple(NumberFormat nf, boolean isCurrency) {
 381         String str = "";
 382         double seed = isCurrency ?  0.0010203040506070809 : 0.00010203040506070809;
 383         double d = (double) -MAX_RANGE;
 384         for (int j = - MAX_RANGE; j <= MAX_RANGE; j++) {
 385             d = d  + 1.0d + seed;
 386             str = nf.format(d);
 387         }
 388         return str;
 389     }
 390 
 391     // This reproduces the throughput load added in benchFormatFairSimple
 392     static double fairSimpleThroughputLoad() {
 393         double seed =  0.00010203040506070809;
 394         double delta = 0.0d;
 395         double d = (double) -MAX_RANGE;
 396         for (int j = - MAX_RANGE; j <= MAX_RANGE; j++) {
 397             d = d + 1.0d + seed;
 398         }
 399         return d;
 400     }
 401 
 402     // Runs fairThroughputLoad and calculate its mean load
 403     static void calculateFairSimpleThroughputLoad() {
 404         int nbRuns = NB_RUNS;
 405         long elapsedTime = 0;
 406         double foo;
 407 
 408         for (int i = 1; i <= nbRuns; i++) {
 409 
 410             long startTime = System.nanoTime();
 411             foo = fairSimpleThroughputLoad();
 412             long estimatedTime = System.nanoTime() - startTime;
 413             if (i > 3) elapsedTime += estimatedTime / 1000;
 414         }
 415 
 416         if (Verbose)
 417         System.out.println(
 418             "calculated throughput load for " + FAIR_SIMPLE_BENCH +
 419             " bench is = " + (elapsedTime / (nbRuns - 3)) + " microseconds");
 420     }
 421 
 422     // ---------- Fractional part is only made of nines bench --------------
 423     private static final String FRACTIONAL_ALL_NINES_BENCH = "benchFormatFractionalAllNines";
 424     private static String benchFormatFractionalAllNines(NumberFormat nf, boolean isCurrency) {
 425         String str = "";
 426         double fractionalEven = isCurrency ?  0.993000001 : 0.99930000001;
 427         double fractionalOdd  = isCurrency ?  0.996000001 : 0.99960000001;
 428         double fractional;
 429         double d;
 430         for (int j = - MAX_RANGE; j <= MAX_RANGE; j++) {
 431             if ((j & 1) == 0)
 432                 fractional = fractionalEven;
 433             else
 434                 fractional = fractionalOdd;
 435             if ( j >= 0)
 436                 d = (double ) j + fractional;
 437             else d = (double) j - fractional;
 438             str = nf.format(d);
 439         }
 440         return str;
 441     }
 442 
 443     // This reproduces the throughput load added in benchFormatFractionalAllNines
 444     static double fractionalAllNinesThroughputLoad() {
 445         double fractionalEven = 0.99930000001;
 446         double fractionalOdd  = 0.99960000001;
 447         double fractional;
 448         double d = 0.0d;
 449         for (int j = - MAX_RANGE; j <= MAX_RANGE; j++) {
 450             if ((j & 1) == 0)
 451                 fractional = fractionalEven;
 452             else fractional = fractionalOdd;
 453             if ( j >= 0)
 454                 d = (double ) j + fractional;
 455             else d = (double) j - fractional;
 456         }
 457         return d;
 458     }
 459 
 460     // Runs fractionalAllNinesThroughputLoad and calculate its mean load
 461     static void calculateFractionalAllNinesThroughputLoad() {
 462         int nbRuns = NB_RUNS;
 463         long elapsedTime = 0;
 464         double foo;
 465 
 466         for (int i = 1; i <= nbRuns; i++) {
 467 
 468             long startTime = System.nanoTime();
 469             foo = fractionalAllNinesThroughputLoad();
 470             long estimatedTime = System.nanoTime() - startTime;
 471             if (i > 3) elapsedTime += estimatedTime / 1000;
 472         }
 473 
 474         if (Verbose)
 475             System.out.println(
 476                "calculated throughput load for " + FRACTIONAL_ALL_NINES_BENCH +
 477                " bench is = " + (elapsedTime / (nbRuns - 3)) + " microseconds");
 478     }
 479 
 480     // ---------- Number is only made of nines bench --------------
 481     private static final String ALL_NINES_BENCH = "benchFormatAllNines";
 482     private static String benchFormatAllNines(NumberFormat nf, boolean isCurrency) {
 483         String str = "";
 484         double[] decimaAllNines =
 485             {9.9993, 99.9993, 999.9993, 9999.9993, 99999.9993,
 486              999999.9993, 9999999.9993, 99999999.9993, 999999999.9993};
 487         double[] currencyAllNines =
 488             {9.993, 99.993, 999.993, 9999.993, 99999.993,
 489              999999.993, 9999999.993, 99999999.993, 999999999.993};
 490         double[] valuesArray = (isCurrency) ? currencyAllNines : decimaAllNines;
 491         double seed = 1.0 / (double) MAX_RANGE;
 492         double d;
 493         int id;
 494         for (int j = - MAX_RANGE; j <= MAX_RANGE; j++) {
 495             id = (j >=  0) ? j % 9 : -j % 9;
 496             if ((j & 1) == 0)
 497                 d = valuesArray[id] + id * seed;
 498             else
 499                 d = valuesArray[id] - id * seed;
 500             str = nf.format(d);
 501         }
 502         return str;
 503     }
 504 
 505     // This reproduces the throughput load added in benchFormatAllNines
 506     static double allNinesThroughputLoad() {
 507         double[] decimaAllNines =
 508             {9.9993, 99.9993, 999.9993, 9999.9993, 99999.9993,
 509              999999.9993, 9999999.9993, 99999999.9993, 999999999.9993};
 510         double[] valuesArray = decimaAllNines;
 511         double seed = 1.0 / (double) MAX_RANGE;
 512         double d = 0.0d;
 513         int id;
 514         for (int j = - MAX_RANGE; j <= MAX_RANGE; j++) {
 515             id = (j >=  0) ? j % 9 : -j % 9;
 516             if ((j & 1) == 0)
 517                 d = valuesArray[id] + id * seed;
 518             else
 519                 d = valuesArray[id] - id * seed;
 520         }
 521         return d;
 522     }
 523 
 524     // Runs allNinesThroughputLoad and calculate its mean load
 525     static void calculateAllNinesThroughputLoad() {
 526         int nbRuns = NB_RUNS;
 527         long elapsedTime = 0;
 528         double foo;
 529 
 530         for (int i = 1; i <= nbRuns; i++) {
 531 
 532             long startTime = System.nanoTime();
 533             foo = allNinesThroughputLoad();
 534             long estimatedTime = System.nanoTime() - startTime;
 535             if (i > 3) elapsedTime += estimatedTime / 1000;
 536         }
 537 
 538         if (Verbose)
 539             System.out.println(
 540                "calculated throughput load for " + ALL_NINES_BENCH +
 541                " bench is = " + (elapsedTime / (nbRuns - 3)) + " microseconds");
 542     }
 543 
 544 
 545 
 546     // --- A fair bench trying (hopefully) to reproduce business applicatons  ---
 547 
 548     /*  benchFormatFair uses the following formula :
 549      *   y = F(x) = sign(x) * x**2 * ((1000/MAX_RANGE)**2).
 550      *
 551      *  which converts in the loop as (if j is the loop index) :
 552      *   x = double(j)
 553      *   k = 1000.0d * double(MAX_RANGE)
 554      *   y = sign(j) * x**2 * k**2
 555      *
 556      *  This is a flattened parabolic curve where only the j values
 557      *  in [-1000, 1000] will provide y results in [-1, +1] interval,
 558      *  and for abs(j) >= 1000 the result y will be greater than 1.
 559      *
 560      *  The difference with benchFormatSmallIntegral is that since y results
 561      *  follow a parabolic curve the magnitude of y grows much more rapidly
 562      *  and closer to j values when abs(j) >= 1000:
 563      *   - for |j| < 1000,  SmallIntegral(j) < 1.0 and fair(j) < 1.0
 564      *   - for j in [1000, 10000[
 565      *      SmallIntegral(j) is in [1, 10[
 566      *      Fair(j) is in [4, 400[
 567      *   - for j in [10000,100000[
 568      *      SmallIntegral(j) is in [10, 100[
 569      *      Fair(j) is in [400,40000[
 570      *   - for j in [100000,1000000[
 571      *      SmallIntegral(j) is in [100, 1000[
 572      *      Fair(j) is in [40000, 4000000[
 573      *
 574      *  Since double values for j less than 100000 provide only 4 digits in the
 575      *  integral, values greater than 250000 provide at least 6 digits, and 500000
 576      *  computes to 1000000, the distribution is roughly half with less than 5
 577      *  digits and half with at least 6 digits in the integral part.
 578      *
 579      *  Compared to FairSimple bench, this represents an application where 20% of
 580      *  the double values to format are less than 40000.0 absolute value.
 581      *
 582      *  Fair(j) is close to the magnitude of j when j > 100000 and is hopefully
 583      *  more representative of what may be found in general in business apps.
 584      *  (assumption : there will be mainly either small or large values, and
 585      *   less values in middle range).
 586      *
 587      *  We could get even more precise distribution of values using formula :
 588      *   y = sign(x) * abs(x)**n * ((1000 / MAX_RANGE)**n) where n > 2,
 589      *  or even well-known statistics function to fine target such distribution,
 590      *  but we have considred that the throughput load for calculating y would
 591      *  then be too high. We thus restrain the use of a power of 2 formula.
 592      */
 593 
 594     private static final String FAIR_BENCH = "benchFormatFair";
 595     private static String benchFormatFair(NumberFormat nf) {
 596         String str = "";
 597         double k = 1000.0d / (double) MAX_RANGE;
 598         k *= k;
 599 
 600         double d;
 601         double absj;
 602         double jPowerOf2;
 603         for (int j = - MAX_RANGE; j <= MAX_RANGE; j++) {
 604             absj = (double) j;
 605             jPowerOf2 = absj * absj;
 606             d = k * jPowerOf2;
 607             if (j < 0) d = -d;
 608             str = nf.format(d);
 609         }
 610         return str;
 611     }
 612 
 613     // This is the exact throughput load added in benchFormatFair
 614     static double fairThroughputLoad() {
 615         double k = 1000.0d / (double) MAX_RANGE;
 616         k *= k;
 617 
 618         double d = 0.0d;
 619         double absj;
 620         double jPowerOf2;
 621         for (int j = - MAX_RANGE; j <= MAX_RANGE; j++) {
 622             absj = (double) j;
 623             jPowerOf2 = absj * absj;
 624             d = k * jPowerOf2;
 625             if (j < 0) d = -d;
 626         }
 627         return d;
 628     }
 629 
 630     // Runs fairThroughputLoad and calculate its mean load
 631     static void calculateFairThroughputLoad() {
 632         int nbRuns = NB_RUNS;
 633         long elapsedTime = 0;
 634         double foo;
 635 
 636         for (int i = 1; i <= nbRuns; i++) {
 637 
 638             long startTime = System.nanoTime();
 639             foo = fairThroughputLoad();
 640             long estimatedTime = System.nanoTime() - startTime;
 641             if (i > 3) elapsedTime += estimatedTime / 1000;
 642         }
 643 
 644         if (Verbose)
 645             System.out.println(
 646                "calculated throughput load for " + FAIR_BENCH +
 647                " bench is = " + (elapsedTime / (nbRuns - 3)) + " microseconds");
 648     }
 649 
 650     // ---------- All double values are very close to a tie --------------------
 651     // i.e. like 123.1235 (for decimal case) or 123.125 (for currency case).
 652 
 653     private static final String TIE_BENCH = "benchFormatTie";
 654     private static String benchFormatTie(NumberFormat nf, boolean isCurrency) {
 655         double d;
 656         String str = "";
 657         double fractionaScaling = (isCurrency) ? 1000.0d : 10000.0d;
 658         int fixedFractionalPart = (isCurrency) ? 125 : 1235;
 659         for (int j = - MAX_RANGE; j <= MAX_RANGE; j++) {
 660             d = (((double) j * fractionaScaling) +
 661                  (double) fixedFractionalPart) / fractionaScaling;
 662             str = nf.format(d);
 663         }
 664         return str;
 665     }
 666 
 667     // This is the exact throughput load added in benchFormatTie
 668     static double tieThroughputLoad(boolean isCurrency) {
 669         double d = 0.0d;
 670         double fractionaScaling = (isCurrency) ? 1000.0d : 10000.0d;
 671         int fixedFractionalPart = (isCurrency) ? 125 : 1235;
 672         for (int j = - MAX_RANGE; j <= MAX_RANGE; j++) {
 673             d = (((double) j * fractionaScaling) +
 674                  (double) fixedFractionalPart) / fractionaScaling;
 675         }
 676         return d;
 677     }
 678 
 679     // Runs tieThroughputLoad and calculate its mean load
 680     static void calculateTieThroughputLoad(boolean isCurrency) {
 681         int nbRuns = NB_RUNS;
 682         long elapsedTime = 0;
 683         double foo;
 684 
 685         for (int i = 1; i <= nbRuns; i++) {
 686 
 687             long startTime = System.nanoTime();
 688             foo = tieThroughputLoad(isCurrency);
 689             long estimatedTime = System.nanoTime() - startTime;
 690             if (i > 3) elapsedTime += estimatedTime / 1000;
 691         }
 692 
 693         if (Verbose)
 694             System.out.println(
 695                "calculated throughput load for " + TIE_BENCH +
 696                " bench is = " + (elapsedTime / (nbRuns - 3)) + " microseconds");
 697     }
 698 
 699 
 700     // Print statistics for passed times results of benchName.
 701     static void printPerfResults(long[] times, String benchName) {
 702         int nbBenches = times.length;
 703 
 704         long totalTimeSpent = 0;
 705         long meanTimeSpent;
 706 
 707         double variance = 0;
 708         double standardDeviation = 0;
 709 
 710         // Calculates mean spent time
 711         for (int i = 1; i <= nbBenches; i++)
 712             totalTimeSpent += times[i-1];
 713         meanTimeSpent = totalTimeSpent / nbBenches;
 714 
 715         // Calculates standard deviation
 716         for (int j = 1; j <= nbBenches; j++)
 717             variance += Math.pow(((double)times[j-1] - (double)meanTimeSpent), 2);
 718         variance = variance / (double) times.length;
 719         standardDeviation = Math.sqrt(variance) / meanTimeSpent;
 720 
 721         // Print result and statistics for benchName
 722         System.out.println(
 723            "Statistics (starting at 4th bench) for bench " + benchName +
 724            "\n for last " + nbBenches +
 725            " runs out of " + NB_RUNS +
 726            " , each with 2x" + MAX_RANGE + " format(double) calls : " +
 727            "\n  mean exec time = " + meanTimeSpent + " microseconds" +
 728            "\n  standard deviation = " + String.format("%.3f", standardDeviation) + "% \n");
 729     }
 730 
 731     public static void main(String[] args) {
 732 
 733         if (args.length >= 1) {
 734             // Parse args, just checks expected ones. Ignore others or dups.
 735             if (args[0].equals("-help")) {
 736                 usage();
 737                 return;
 738             }
 739 
 740             for (String s : args) {
 741                 if (s.equals("-doit"))
 742                     DoIt = true;
 743                 else if (s.equals("-verbose"))
 744                     Verbose = true;
 745             }
 746         } else {
 747             // No arguments, skips the benchmarks and exits.
 748             System.out.println(
 749                 "Test skipped with success by default. See -help for details.");
 750             return;
 751         }
 752 
 753         if (!DoIt) {
 754             if (Verbose)
 755                 usage();
 756             System.out.println(
 757                 "Test skipped and considered successful.");
 758             return;
 759         }
 760 
 761         System.out.println("Single Threaded micro benchmark evaluating " +
 762                            "the throughput of java.text.DecimalFormat.format() call stack.\n");
 763 
 764         String fooString = "";
 765 
 766         // Run benches for decimal instance
 767         DecimalFormat df = (DecimalFormat) NumberFormat.getInstance(Locale.US);
 768         System.out.println("Running with a decimal instance of DecimalFormat.");
 769 
 770         calculateIntegerThroughputLoad();
 771         fooString =
 772             BenchType.INTEGER_BENCH.runBenchAndPrintStatistics(NB_RUNS, df, false);
 773 
 774         calculateFractionalThroughputLoad();
 775         fooString =
 776             BenchType.FRACTIONAL_BENCH.runBenchAndPrintStatistics(NB_RUNS, df, false);
 777 
 778         calculateSmallIntegralThroughputLoad();
 779         fooString =
 780             BenchType.SMALL_INTEGRAL_BENCH.runBenchAndPrintStatistics(NB_RUNS, df, false);
 781 
 782         calculateFractionalAllNinesThroughputLoad();
 783         fooString =
 784             BenchType.FRACTIONAL_ALL_NINES_BENCH.runBenchAndPrintStatistics(NB_RUNS, df, false);
 785 
 786         calculateAllNinesThroughputLoad();
 787         fooString =
 788             BenchType.ALL_NINES_BENCH.runBenchAndPrintStatistics(NB_RUNS, df, false);
 789 
 790         calculateFairSimpleThroughputLoad();
 791         fooString =
 792             BenchType.FAIR_SIMPLE_BENCH.runBenchAndPrintStatistics(NB_RUNS, df, false);
 793 
 794         calculateFairThroughputLoad();
 795         fooString =
 796             BenchType.FAIR_BENCH.runBenchAndPrintStatistics(NB_RUNS, df, false);
 797 
 798         calculateTieThroughputLoad(false);
 799         fooString =
 800             BenchType.TIE_BENCH.runBenchAndPrintStatistics(NB_RUNS, df, false);
 801 
 802         // Run benches for currency instance
 803         DecimalFormat cf = (DecimalFormat) NumberFormat.getCurrencyInstance(Locale.US);
 804         System.out.println("Running with a currency instance of DecimalFormat.");
 805 
 806         calculateIntegerThroughputLoad();
 807         fooString =
 808             BenchType.INTEGER_BENCH.runBenchAndPrintStatistics(NB_RUNS, cf, false);
 809 
 810         calculateFractionalThroughputLoad();
 811         fooString =
 812             BenchType.FRACTIONAL_BENCH.runBenchAndPrintStatistics(NB_RUNS, cf, false);
 813 
 814         calculateSmallIntegralThroughputLoad();
 815         fooString =
 816             BenchType.SMALL_INTEGRAL_BENCH.runBenchAndPrintStatistics(NB_RUNS, cf, false);
 817 
 818         calculateFractionalAllNinesThroughputLoad();
 819         fooString =
 820             BenchType.FRACTIONAL_ALL_NINES_BENCH.runBenchAndPrintStatistics(NB_RUNS, cf, false);
 821 
 822         calculateAllNinesThroughputLoad();
 823         fooString =
 824             BenchType.ALL_NINES_BENCH.runBenchAndPrintStatistics(NB_RUNS, cf, false);
 825 
 826         calculateFairSimpleThroughputLoad();
 827         fooString =
 828             BenchType.FAIR_SIMPLE_BENCH.runBenchAndPrintStatistics(NB_RUNS, cf, false);
 829 
 830         calculateFairThroughputLoad();
 831         fooString =
 832             BenchType.FAIR_BENCH.runBenchAndPrintStatistics(NB_RUNS, cf, false);
 833 
 834         calculateTieThroughputLoad(false);
 835         fooString =
 836             BenchType.TIE_BENCH.runBenchAndPrintStatistics(NB_RUNS, cf, false);
 837 
 838     }
 839 
 840     // This class to factorise what would be duplicated otherwise.
 841     static enum BenchType {
 842 
 843         INTEGER_BENCH("benchFormatInteger"),
 844         FRACTIONAL_BENCH("benchFormatFractional"),
 845         SMALL_INTEGRAL_BENCH("benchFormatSmallIntegral"),
 846         FAIR_SIMPLE_BENCH("benchFormatFairSimple"),
 847         FRACTIONAL_ALL_NINES_BENCH("benchFormatFractionalAllNines"),
 848         ALL_NINES_BENCH("benchFormatAllNines"),
 849         FAIR_BENCH("benchFormatFair"),
 850         TIE_BENCH("benchFormatTie");
 851 
 852         private final String name;
 853 
 854         BenchType(String name) {
 855             this.name = name;
 856         }
 857 
 858         String runBenchAndPrintStatistics(int nbRuns,
 859                          NumberFormat nf,
 860                          boolean isCurrency) {
 861 
 862             // We eliminate the first 3 runs in the time measurements
 863             // to let C2 do complete compilation and optimization work.
 864             long[] elapsedTimes = new long[nbRuns - 3];
 865 
 866             System.out.println("Now running " + nbRuns + " times bench " + name);
 867 
 868             String str = "";
 869             for (int i = 1; i <= nbRuns; i++) {
 870 
 871                 stabilizeMemory(false);
 872                 long startTime = System.nanoTime();
 873 
 874                 switch(this) {
 875                 case INTEGER_BENCH :
 876                     str = benchFormatInteger(nf);
 877                     break;
 878                 case FRACTIONAL_BENCH :
 879                     str = benchFormatFractional(nf);
 880                     break;
 881                 case SMALL_INTEGRAL_BENCH :
 882                     str = benchFormatSmallIntegral(nf);
 883                     break;
 884                 case FRACTIONAL_ALL_NINES_BENCH :
 885                     str = benchFormatFractionalAllNines(nf, isCurrency);
 886                     break;
 887                 case ALL_NINES_BENCH :
 888                     str = benchFormatAllNines(nf, isCurrency);
 889                     break;
 890                 case FAIR_SIMPLE_BENCH :
 891                     str = benchFormatFairSimple(nf, isCurrency);
 892                     break;
 893                 case FAIR_BENCH :
 894                     str = benchFormatFair(nf);
 895                     break;
 896                 case TIE_BENCH :
 897                     str = benchFormatTie(nf, isCurrency);
 898                     break;
 899 
 900                 default:
 901                 }
 902 
 903 
 904                 long estimatedTime = System.nanoTime() - startTime;
 905                 if (i > 3)
 906                     elapsedTimes[i-4] = estimatedTime / 1000;
 907 
 908                 if (Verbose)
 909                     System.out.println(
 910                                        "calculated time for " + name +
 911                                        " bench " + i + " is = " +
 912                                        (estimatedTime / 1000) + " microseconds");
 913                 else System.out.print(".");
 914 
 915                 stabilizeMemory(true);
 916             }
 917 
 918             System.out.println(name + " Done.");
 919 
 920             printPerfResults(elapsedTimes, name);
 921 
 922             return str;
 923         }
 924     }
 925 
 926 }