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++;