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 }