<i18n dev> Review Request for CR : 7050528 Improve performance of java.text.DecimalFormat.format() call stack

Joe Darcy joe.darcy at oracle.com
Tue Sep 13 23:04:44 PDT 2011


Hello Olivier.

Olivier Lagneau wrote:
> Please review The implementation of a fast-path algorithm for 
> optimizing the
> DecimalFormat(double, ...) call stack.
>
> The webrev is here :
> http://cr.openjdk.java.net/~alanb/7050528/webrev.01
>
> As described in the CR evaluation and suggested fix,
> the speed-up on a micro-benchmark is ~x11 on Sparc-USIV arch and ~x10 
> on Sparc-T4.
> The footprint cost is ~6kbytes of static char array constant data to 
> collect digits from integer values.
>
> New test dedicated at checking fast-path algorithm, as well as 
> micro-benchmark,
> are coming soon in a subsequent webrev containing them.
>

I have some initial comments on your changes.

First, since the DecimalFormat class is serializable all the new fields 
should be transient and the readObject method should be updated to 
initialize them accordingly.  However, I suggest putting the fast-path 
specific data into a package private static nested class of 
DecimalFormat and having a single transient pointer in DecimalFormat to 
the helper object.

Since the general contract of NumberFormat is for external 
synchronization, it it not incorrect that the various new mutable fields 
are not declared to be volatile (or final).

Also in terms of organizing data, I suggest using the initialize on 
demand holder class idiom for 6k of "The Fast Path for double Constants" 
info.  That is, create a static nested class to store the data.  This 
pattern ensures that the data is not loaded unnecessary and avoids the 
need for synchronization when the data is accessed.

I've attached a patch below which changes the code to more closely 
follow JDK whitespace conventions.  It also changes a number of 
instances of

if (condition)
    foo = true;
else
    foo = false;

to

    foo = condition;

as well as making the new fields transient (no readObject update) and 
using a sun.mis.FpUtils.isFinite method.  Diff below.

Cheers,

-Joe

523c523,524
<         // If fieldPosition is a DontCareFieldPosition instance we can 
try to go to fast-path code.
---
 >         // If fieldPosition is a DontCareFieldPosition instance we can
 >         // try to go to fast-path code.
525c526,527
<         if (fieldPosition == DontCareFieldPosition.INSTANCE) 
tryFastPath = true;
---
 >         if (fieldPosition == DontCareFieldPosition.INSTANCE)
 >             tryFastPath = true;
892c894,895
<         else if (fractionalPartAsInt != 0) boundIndex = 
nbFractionalDigits;
---
 >         else if (fractionalPartAsInt != 0)
 >             boundIndex = nbFractionalDigits;
909c912
<             // Applies healf-even rounding rule.
---
 >             // Applies half-even rounding rule.
912c915,916
<             else return (number > perfectHalf);
---
 >             else
 >                 return (number > perfectHalf);
916,917c920,922
<     // Returns the exact number of digits (radix 10) representing
<     //  the passed paramater i. Sets utility formating fields used in 
the algorithm
---
 >     // Returns the exact number of digits (radix 10) representing the
 >     // passed paramater i. Sets utility formating fields used in the
 >     // algorithm
927,930c932,933
<                     }
<                     else {
<                         if (i == 99) isAllNines = true;
<                         else isAllNines = false;
---
 >                     } else {
 >                         isAllNines = (i == 99);
934,937c937,938
<                 }
<                 else {
<                      if (i == 999) isAllNines = true;
<                      else isAllNines = false;
---
 >                 } else {
 >                     isAllNines = (i == 999);
943,944c944
<                 if (i == 9999) isAllNines = true;
<                 else isAllNines = false;
---
 >                 isAllNines = (i == 9999);
947,950c947,948
<             }
<             else {
<                 if (i == 99999) isAllNines = true;
<                 else isAllNines = false;
---
 >             } else {
 >                 isAllNines = (i == 99999);
954,955c952
<         }
<         else {
---
 >         } else {
958,959c955
<                     if (i == 999999) isAllNines = true;
<                     else isAllNines = false;
---
 >                     isAllNines = (i == 999999);
962,965c958,959
<                 }
<                 else if (i <= 9999999) {
<                     if (i == 9999999) isAllNines = true;
<                     else isAllNines = false;
---
 >                 } else if (i <= 9999999) {
 >                     isAllNines = (i == 9999999);
968,971c962,963
<                 }
<                 else {
<                     if (i == 99999999) isAllNines = true;
<                     else isAllNines = false;
---
 >                 } else {
 >                     isAllNines = (i == 99999999);
975,978c967,968
<             }
<             else if (i <= 999999999) {
<                 if (i == 999999999) isAllNines = true;
<                 else isAllNines = false;
---
 >             } else if (i <= 999999999) {
 >                 isAllNines = (i == 999999999);
981,982c971
<             }
<             else {
---
 >             } else {
994c983,984
<             if (i == 999) isAllNines = true;
---
 >             if (i == 999)
 >                 isAllNines = true;
996,998c986,988
<         }
<         else if (i < 10) {
<             if (i == 9) isAllNines = true;
---
 >         } else if (i < 10) {
 >             if (i == 9)
 >                 isAllNines = true;
1000,1002c990,992
<         }
<         else {
<             if (i == 99) isAllNines = true;
---
 >         } else {
 >             if (i == 99)
 >                 isAllNines = true;
1008,1009c998,1000
<     // Extracts and returns the 2 (Currency pattern) or 3 (Decimal 
pattern)
<     //  digits int representing the fractional part of passed double.
---
 >     // Extracts and returns the 2 (Currency pattern) or 3 (Decimal
 >     // pattern) digits int representing the fractional part of passed
 >     // double.
1017,1018c1008
<         }
<         else {
---
 >         } else {
1030d1019
<
1058,1060c1047,1048
<             if ((minimumIntegerDigits == 1) &&
<                 (maximumIntegerDigits >= 10)) isFastPath = true;
<             else isFastPath = false;
---
 >             isFastPath = ((minimumIntegerDigits == 1) &&
 >                           (maximumIntegerDigits >= 10));
1067,1070c1055,1059
<                         (maximumFractionDigits != 2)) isFastPath = false;
<                 }
<                 else if ((minimumFractionDigits != 0) ||
<                          (maximumFractionDigits != 3)) isFastPath = false;
---
 >                         (maximumFractionDigits != 2))
 >                         isFastPath = false;
 >                 } else if ((minimumFractionDigits != 0) ||
 >                            (maximumFractionDigits != 3))
 >                     isFastPath = false;
1072,1073c1061,1062
<         }
<         else isFastPath = false;
---
 >         } else
 >             isFastPath = false;
1121,1122c1110
<             }
<             else {
---
 >             } else {
1126,1127c1114
<         }
<         else if (fastPathWasOn) {
---
 >         } else if (fastPathWasOn) {
1145c1132,1133
<         if (len == 1) digitsBuffer[lowerIndex] = digit;
---
 >         if (len == 1)
 >             digitsBuffer[lowerIndex] = digit;
1154c1142,1143
<                 if (--upperIndex > lowerIndex) 
digitsBuffer[upperIndex] = digit;
---
 >                 if (--upperIndex > lowerIndex)
 >                     digitsBuffer[upperIndex] = digit;
1165c1154,1155
<         else suffix = charsPositiveSuffix;
---
 >         else
 >             suffix = charsPositiveSuffix;
1169c1159,1160
<         if (len == 0) return;
---
 >         if (len == 0)
 >             return;
1181,1184c1172,1177
<             if (len > 2) internalChars[dstLower] = suffix[1];
<             if (len == 4) internalChars[dstUpper] = suffix[2];
<         }
<         else System.arraycopy(suffix, 0, internalChars, startIndex, len);
---
 >             if (len > 2)
 >                 internalChars[dstLower] = suffix[1];
 >             if (len == 4)
 >                 internalChars[dstUpper] = suffix[2];
 >         } else
 >             System.arraycopy(suffix, 0, internalChars, startIndex, len);
1192c1185,1186
<         // We localize digits only, taking into account 
currency/decimal fractional case
---
 >         // We localize digits only, taking into account
 >         // currency/decimal fractional case
1199,1201c1193,1195
<             }
<             else {
<                 // Cursor char is decimal separator or grouping char. 
Just reinit counter.
---
 >             } else {
 >                 // Cursor char is decimal separator or grouping
 >                 // char. Just reinit counter.
1212c1206,1207
<         if (!toBeRounded) fillDigit = '9';
---
 >         if (!toBeRounded)
 >             fillDigit = '9';
1234,1235c1229,1230
<             }
<             else break;
---
 >             } else
 >                 break;
1239,1240c1234,1237
<         if (!toBeRounded || isCurrencyFormat) lastUsedIndex += 
fractionalLength;
<         else lastUsedIndex--; // fractional part absent
---
 >         if (!toBeRounded || isCurrencyFormat)
 >             lastUsedIndex += fractionalLength;
 >         else
 >             lastUsedIndex--; // fractional part absent
1269,1270c1266,1267
<             }
<             else digitsBuffer[index]  = DigitTens1000[number];
---
 >             } else
 >                 digitsBuffer[index]  = DigitTens1000[number];
1289,1290c1286
<         }
<         else {
---
 >         } else {
1292c1288,1289
<             if (number == 0) index -= 4;
---
 >             if (number == 0)
 >                 index -= 4;
1302,1303c1299
<                 }
<                 else {
---
 >                 } else {
1352,1353c1348
<             }
<             else {
---
 >             } else {
1368,1369c1363
<                     }
<                     else {
---
 >                     } else {
1373,1374c1367
<                 }
<                 else {
---
 >                 } else {
1386c1379,1380
<         else formatAllNinesCase(internalChars, startIndex,
---
 >         else {
 >             formatAllNinesCase(internalChars, startIndex,
1388a1383
 >         }
1391c1386,1387
<         if (zeroDelta != 0) localizeDigits(internalChars, startIndex);
---
 >         if (zeroDelta != 0)
 >             localizeDigits(internalChars, startIndex);
1394c1390,1391
<         if (isSuffixPresent) appendSuffix(negative, internalChars);
---
 >         if (isSuffixPresent)
 >             appendSuffix(negative, internalChars);
1411c1408,1409
<         if (fastPathCheckNeeded) checkAndSetFastPathStatus();
---
 >         if (fastPathCheckNeeded)
 >             checkAndSetFastPathStatus();
1414,1416c1412,1413
<             if (!Double.isNaN(number) &&
<                 !Double.isInfinite(number)) {
<
---
 >             // If finite
 >             if (sun.misc.FpUtils.isFinite(number)) {
1427,1428c1424
<                 }
<                 else if (number < 0.0) { // 2 tests for negative doubles
---
 >                 } else if (number < 0.0) { // 2 tests for negative 
doubles
1436,1437c1432
<                 }
<                 else if ( 1/number > 0.0) { // 3 tests for positive 
zero. Not frequent.
---
 >                 } else if ( 1/number > 0.0) { // 3 tests for positive 
zero. Not frequent.
1442,1443c1437
<                 }
<                 else { // 4 tests for negative zero (hopefully very rare)
---
 >                 } else { // 4 tests for negative zero (hopefully very 
rare)
3828c3822
<     private boolean isAllNines = false;
---
 >     private transient boolean isAllNines = false;
3831c3825
<     private double remainingFractionalPart = 0.0d;
---
 >     private transient double remainingFractionalPart = 0.0d;
3834c3828
<     private int lastUsedIndex = 0;
---
 >     private transient int lastUsedIndex = 0;
3837c3831
<     private int nbGroupsNeeded = 0;
---
 >     private transient int nbGroupsNeeded = 0;
3842c3836
<     private boolean isFastPath = false;
---
 >     private transient boolean isFastPath = false;
3845c3839
<     private boolean fastPathCheckNeeded = true;
---
 >     private transient boolean fastPathCheckNeeded = true;
3848,3849c3842,3843
<     private char[] positiveFastPathContainer;
<     private char[] negativeFastPathContainer;
---
 >     private transient char[] positiveFastPathContainer;
 >     private transient char[] negativeFastPathContainer;
3852,3853c3846,3847
<     private double[] safeMinusRoundingBounds = null;
<     private double[] safeMajorRoundingBounds = null;
---
 >     private transient double[] safeMinusRoundingBounds = null;
 >     private transient double[] safeMajorRoundingBounds = null;
3856,3858c3850,3852
<     private boolean isSuffixPresent;
<     private char[] charsPositiveSuffix;
<     private char[] charsNegativeSuffix;
---
 >     private transient boolean isSuffixPresent;
 >     private transient char[] charsPositiveSuffix;
 >     private transient char[] charsNegativeSuffix;
3861c3855
<     private int  zeroDelta;
---
 >     private transient int  zeroDelta;
3864c3858
<     private char groupingChar;
---
 >     private transient char groupingChar;
3867c3861
<     private char decimalSeparatorChar;
---
 >     private transient char decimalSeparatorChar;
3921,3922c3915,3918
<             if (digitOne == '9') digitOne = '0';
<             else digitOne++;
---
 >             if (digitOne == '9')
 >                 digitOne = '0';
 >             else
 >                 digitOne++;
3927,3928c3923,3926
<                 if (digitTen == '9') digitTen = '0';
<                 else digitTen++;
---
 >                 if (digitTen == '9')
 >                     digitTen = '0';
 >                 else
 >                     digitTen++;



More information about the i18n-dev mailing list