Skip to content

Commit

Permalink
Implement lookup of hex digits with branchless code.
Browse files Browse the repository at this point in the history
Simplify isDigit() methods.
  • Loading branch information
wrandelshofer committed Feb 26, 2023
1 parent d5a924b commit 85a1076
Show file tree
Hide file tree
Showing 29 changed files with 455 additions and 334 deletions.
76 changes: 38 additions & 38 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,21 +107,21 @@ Most input lines look like this: `0.4011441469603171`.

|Method | MB/s |stdev|Mfloats/s| ns/f | speedup | JDK |
|---------------------------|------:|-----:|------:|--------:|--------:|--------|
|java.lang.Double | 91.07| 3.8 %| 5.23| 191.31| 1.00|20-ea |
|java.lang.Float | 96.15| 7.3 %| 5.52| 181.20| 1.00|20-ea |
|java.math.BigDecimal | 192.26| 8.0 %| 11.03| 90.62| 1.00|20-ea |
|JavaDoubleParser String | 400.80|14.9 %| 23.00| 43.47| 4.40|20-ea |
|JavaDoubleParser char[] | 520.21|14.2 %| 29.86| 33.49| 5.71|20-ea |
|JavaDoubleParser byte[] | 593.21|21.2 %| 34.05| 29.37| 6.51|20-ea |
|JsonDoubleParser String | 411.79|13.6 %| 23.64| 42.31| 4.52|20-ea |
|JsonDoubleParser char[] | 562.95|14.9 %| 32.31| 30.95| 6.18|20-ea |
|JsonDoubleParser byte[] | 613.03|14.0 %| 35.19| 28.42| 6.73|20-ea |
|JavaFloatParser String | 367.07|12.2 %| 21.07| 47.46| 3.82|20-ea |
|JavaFloatParser char[] | 518.64|11.4 %| 29.77| 33.59| 5.39|20-ea |
|JavaFloatParser byte[] | 613.75| 9.3 %| 35.23| 28.39| 6.38|20-ea |
|JavaBigDecimalParser String| 398.83|14.1 %| 22.89| 43.68| 2.07|20-ea |
|JavaBigDecimalParser char[]| 557.90|14.3 %| 32.02| 31.23| 2.90|20-ea |
|JavaBigDecimalParser byte[]| 652.20|16.6 %| 37.43| 26.71| 3.39|20-ea |
|java.lang.Double | 91.73|10.1 %| 5.26| 189.95| 1.00|20 |
|java.lang.Float | 92.93| 6.1 %| 5.33| 187.50| 1.00|20 |
|java.math.BigDecimal | 172.14| 7.2 %| 9.88| 101.22| 1.00|20 |
|JavaDoubleParser String | 554.66| 4.0 %| 31.83| 31.41| 6.05|20 |
|JavaDoubleParser char[] | 592.95|13.4 %| 34.03| 29.39| 6.46|20 |
|JavaDoubleParser byte[] | 642.69| 3.6 %| 36.88| 27.11| 7.01|20 |
|JsonDoubleParser String | 551.93|17.4 %| 31.68| 31.57| 6.02|20 |
|JsonDoubleParser char[] | 612.85|14.2 %| 35.17| 28.43| 6.68|20 |
|JsonDoubleParser byte[] | 642.12| 4.6 %| 36.85| 27.14| 7.00|20 |
|JavaFloatParser String | 519.72|16.3 %| 29.83| 33.53| 5.59|20 |
|JavaFloatParser char[] | 599.44| 5.6 %| 34.40| 29.07| 6.45|20 |
|JavaFloatParser byte[] | 621.22|11.2 %| 35.65| 28.05| 6.68|20 |
|JavaBigDecimalParser String| 513.70|17.4 %| 29.48| 33.92| 2.98|20 |
|JavaBigDecimalParser char[]| 617.66|25.9 %| 35.45| 28.21| 3.59|20 |
|JavaBigDecimalParser byte[]| 670.74| 5.8 %| 38.49| 25.98| 3.90|20 |

### The data file `canada.txt`

Expand All @@ -130,21 +130,21 @@ Most input lines look like this: `52.038048000000117`.

|Method | MB/s |stdev|Mfloats/s| ns/f | speedup | JDK |
|---------------------------|------:|-----:|------:|--------:|--------:|--------|
|java.lang.Double | 71.28| 9.7 %| 4.10| 244.12| 1.00|20-ea |
|java.lang.Float | 87.45| 7.1 %| 5.03| 198.98| 1.00|20-ea |
|java.math.BigDecimal | 244.03|10.9 %| 14.02| 71.31| 1.00|20-ea |
|JavaDoubleParser String | 294.45|13.1 %| 16.92| 59.10| 4.13|20-ea |
|JavaDoubleParser char[] | 419.26|13.0 %| 24.09| 41.51| 5.88|20-ea |
|JavaDoubleParser byte[] | 461.00|20.0 %| 26.49| 37.75| 6.47|20-ea |
|JsonDoubleParser String | 312.11|16.1 %| 17.94| 55.75| 4.38|20-ea |
|JsonDoubleParser char[] | 381.20|22.6 %| 21.91| 45.65| 5.35|20-ea |
|JsonDoubleParser byte[] | 463.28|20.2 %| 26.62| 37.56| 6.50|20-ea |
|JavaFloatParser String | 285.14|13.0 %| 16.39| 61.03| 3.26|20-ea |
|JavaFloatParser char[] | 411.34|20.3 %| 23.64| 42.30| 4.70|20-ea |
|JavaFloatParser byte[] | 526.98|16.9 %| 30.28| 33.02| 6.03|20-ea |
|JavaBigDecimalParser String| 288.74|23.5 %| 16.59| 60.27| 1.18|20-ea |
|JavaBigDecimalParser char[]| 442.03|14.5 %| 25.40| 39.37| 1.81|20-ea |
|JavaBigDecimalParser byte[]| 457.09|21.1 %| 26.27| 38.07| 1.87|20-ea |
|java.lang.Double | 80.33| 5.2 %| 4.62| 216.62| 1.00|20 |
|java.lang.Float | 94.04| 4.7 %| 5.40| 185.04| 1.00|20 |
|java.math.BigDecimal | 298.46|11.9 %| 17.15| 58.30| 1.00|20 |
|JavaDoubleParser String | 366.14|13.9 %| 21.04| 47.53| 4.56|20 |
|JavaDoubleParser char[] | 590.31| 4.8 %| 33.92| 29.48| 7.35|20 |
|JavaDoubleParser byte[] | 548.01|12.2 %| 31.49| 31.75| 6.82|20 |
|JsonDoubleParser String | 404.25|14.8 %| 23.23| 43.05| 5.03|20 |
|JsonDoubleParser char[] | 575.44| 3.5 %| 33.07| 30.24| 7.16|20 |
|JsonDoubleParser byte[] | 576.21| 3.2 %| 33.11| 30.20| 7.17|20 |
|JavaFloatParser String | 342.41|14.5 %| 19.68| 50.82| 3.64|20 |
|JavaFloatParser char[] | 576.07|16.1 %| 33.10| 30.21| 6.13|20 |
|JavaFloatParser byte[] | 592.73|12.2 %| 34.06| 29.36| 6.30|20 |
|JavaBigDecimalParser String| 420.41|15.4 %| 24.16| 41.39| 1.41|20 |
|JavaBigDecimalParser char[]| 643.92|18.4 %| 37.00| 27.02| 2.16|20 |
|JavaBigDecimalParser byte[]| 686.99| 5.8 %| 39.48| 25.33| 2.30|20 |

### The data file `mesh.txt`

Expand Down Expand Up @@ -175,14 +175,14 @@ Most input lines look like this: `-0x1.09219008205fcp6`.

|Method | MB/s |stdev|Mfloats/s| ns/f | speedup | JDK |
|---------------------------|------:|-----:|------:|--------:|--------:|--------|
|java.lang.Double | 35.74| 3.4 %| 1.96| 510.34| 1.00|20 |
|java.lang.Float | 35.76| 3.3 %| 1.96| 510.04| 1.00|20 |
|JavaDoubleParser String | 312.41|11.2 %| 17.13| 58.38| 8.74|20 |
|JavaDoubleParser char[] | 443.68|16.9 %| 24.33| 41.11| 12.41|20 |
|JavaDoubleParser byte[] | 497.49|14.5 %| 27.28| 36.66| 13.92|20 |
|JavaFloatParser String | 313.17|11.5 %| 17.17| 58.24| 8.76|20 |
|JavaFloatParser char[] | 434.04|16.2 %| 23.80| 42.02| 12.14|20 |
|JavaFloatParser byte[] | 494.68|14.7 %| 27.12| 36.87| 13.83|20 |
|java.lang.Double | 36.60| 7.1 %| 2.01| 498.35| 1.00|20 |
|java.lang.Float | 37.47| 5.0 %| 2.05| 486.78| 1.00|20 |
|JavaDoubleParser String | 387.82|11.9 %| 21.26| 47.03| 10.60|20 |
|JavaDoubleParser char[] | 535.71|10.9 %| 29.37| 34.04| 14.64|20 |
|JavaDoubleParser byte[] | 523.70| 9.4 %| 28.71| 34.83| 14.31|20 |
|JavaFloatParser String | 404.32|12.4 %| 22.17| 45.11| 10.79|20 |
|JavaFloatParser char[] | 531.39| 3.3 %| 29.14| 34.32| 14.18|20 |
|JavaFloatParser byte[] | 519.60| 9.6 %| 28.49| 35.10| 13.87|20 |

### Comparison with C version

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,38 +34,5 @@ abstract class AbstractFloatValueParser extends AbstractNumberParser {
* The hexadecimal exponent of a double has a range of -1022 to +1023.
*/
final static int MAX_EXPONENT_NUMBER = 1024;
/**
* Special value in {@link #CHAR_TO_HEX_MAP} for
* the decimal point character.
*/
static final byte DECIMAL_POINT_CLASS = -4;
/**
* Special value in {@link #CHAR_TO_HEX_MAP} for
* characters that are neither a hex digit nor
* a decimal point character..
*/
static final byte OTHER_CLASS = -1;
/**
* Includes all non-negative values of a {@code byte}, so that we only have
* to check for byte values {@literal <} 0 before accessing this array.
*/
static final byte[] CHAR_TO_HEX_MAP = new byte[128];

static {
for (char ch = 0; ch < AbstractFloatValueParser.CHAR_TO_HEX_MAP.length; ch++) {
AbstractFloatValueParser.CHAR_TO_HEX_MAP[ch] = AbstractFloatValueParser.OTHER_CLASS;
}
for (char ch = '0'; ch <= '9'; ch++) {
AbstractFloatValueParser.CHAR_TO_HEX_MAP[ch] = (byte) (ch - '0');
}
for (char ch = 'A'; ch <= 'F'; ch++) {
AbstractFloatValueParser.CHAR_TO_HEX_MAP[ch] = (byte) (ch - 'A' + 10);
}
for (char ch = 'a'; ch <= 'f'; ch++) {
AbstractFloatValueParser.CHAR_TO_HEX_MAP[ch] = (byte) (ch - 'a' + 10);
}
for (char ch = '.'; ch <= '.'; ch++) {
AbstractFloatValueParser.CHAR_TO_HEX_MAP[ch] = AbstractFloatValueParser.DECIMAL_POINT_CLASS;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -111,18 +111,18 @@ private long parseDecFloatLiteral(byte[] str, int index, int startIndex, int end
// ---------------------
int expNumber = 0;
if (ch == 'e' || ch == 'E') {
ch = ++index < endIndex ? str[index] : 0;
ch = charAt(str, ++index, endIndex);
boolean isExponentNegative = ch == '-';
if (isExponentNegative || ch == '+') {
ch = ++index < endIndex ? str[index] : 0;
ch = charAt(str, ++index, endIndex);
}
illegal |= !FastDoubleSwar.isDigit(ch);
do {
// Guard against overflow
if (expNumber < AbstractFloatValueParser.MAX_EXPONENT_NUMBER) {
expNumber = 10 * expNumber + ch - '0';
}
ch = ++index < endIndex ? str[index] : 0;
ch = charAt(str, ++index, endIndex);
} while (FastDoubleSwar.isDigit(ch));
if (isExponentNegative) {
expNumber = -expNumber;
Expand Down Expand Up @@ -174,6 +174,7 @@ private long parseDecFloatLiteral(byte[] str, int index, int startIndex, int end
exponentOfTruncatedSignificand);
}


/**
* Parses a {@code FloatingPointLiteral} production with optional leading and trailing
* white space.
Expand Down Expand Up @@ -210,7 +211,7 @@ public long parseFloatingPointLiteral(byte[] str, int offset, int length) {
// -------------------
final boolean isNegative = ch == '-';
if (isNegative || ch == '+') {
ch = ++index < endIndex ? str[index] : 0;
ch = charAt(str, ++index, endIndex);
if (ch == 0) {
throw new NumberFormatException(SYNTAX_ERROR);
}
Expand All @@ -226,7 +227,7 @@ public long parseFloatingPointLiteral(byte[] str, int offset, int length) {
// ---------------------------
final boolean hasLeadingZero = ch == '0';
if (hasLeadingZero) {
ch = ++index < endIndex ? str[index] : 0;
ch = charAt(str, ++index, endIndex);
if (ch == 'x' || ch == 'X') {
return parseHexFloatingPointLiteral(str, index + 1, offset, endIndex, isNegative);
}
Expand Down Expand Up @@ -273,7 +274,7 @@ private long parseHexFloatingPointLiteral(
for (; index < endIndex; index++) {
ch = str[index];
// Table look up is faster than a sequence of if-else-branches.
int hexValue = ch < 0 ? AbstractFloatValueParser.OTHER_CLASS : AbstractFloatValueParser.CHAR_TO_HEX_MAP[ch];
int hexValue = lookupHex(ch);
if (hexValue >= 0) {
significand = (significand << 4) | hexValue;// This might overflow, we deal with it later.
} else if (hexValue == AbstractFloatValueParser.DECIMAL_POINT_CLASS) {
Expand Down Expand Up @@ -308,18 +309,18 @@ private long parseHexFloatingPointLiteral(
int expNumber = 0;
final boolean hasExponent = (ch == 'p') || (ch == 'P');
if (hasExponent) {
ch = ++index < endIndex ? str[index] : 0;
ch = charAt(str, ++index, endIndex);
boolean isExponentNegative = ch == '-';
if (isExponentNegative || ch == '+') {
ch = ++index < endIndex ? str[index] : 0;
ch = charAt(str, ++index, endIndex);
}
illegal |= !FastDoubleSwar.isDigit(ch);
do {
// Guard against overflow
if (expNumber < AbstractFloatValueParser.MAX_EXPONENT_NUMBER) {
expNumber = 10 * (expNumber) + ch - '0';
}
ch = ++index < endIndex ? str[index] : 0;
ch = charAt(str, ++index, endIndex);
} while (FastDoubleSwar.isDigit(ch));
if (isExponentNegative) {
expNumber = -expNumber;
Expand Down Expand Up @@ -352,7 +353,7 @@ private long parseHexFloatingPointLiteral(
for (index = significandStartIndex; index < significandEndIndex; index++) {
ch = str[index];
// Table look up is faster than a sequence of if-else-branches.
int hexValue = ch < 0 ? AbstractFloatValueParser.OTHER_CLASS : AbstractFloatValueParser.CHAR_TO_HEX_MAP[ch];
int hexValue = lookupHex(ch);
if (hexValue >= 0) {
if (Long.compareUnsigned(significand, AbstractFloatValueParser.MINIMAL_NINETEEN_DIGIT_INTEGER) < 0) {
significand = (significand << 4) | hexValue;
Expand All @@ -372,6 +373,7 @@ private long parseHexFloatingPointLiteral(
(virtualIndexOfPoint - index + skipCountInTruncatedDigits) * 4 + expNumber);
}


private long parseNaNOrInfinity(byte[] str, int index, int endIndex, boolean isNegative) {
if (str[index] == 'N') {
if (index + 2 < endIndex
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -115,18 +115,18 @@ private long parseDecFloatLiteral(char[] str, int index, int startIndex, int end
// ---------------------
int expNumber = 0;
if (ch == 'e' || ch == 'E') {
ch = ++index < endIndex ? str[index] : 0;
ch = charAt(str, ++index, endIndex);
boolean isExponentNegative = ch == '-';
if (isExponentNegative || ch == '+') {
ch = ++index < endIndex ? str[index] : 0;
ch = charAt(str, ++index, endIndex);
}
illegal |= !FastDoubleSwar.isDigit(ch);
do {
// Guard against overflow
if (expNumber < AbstractFloatValueParser.MAX_EXPONENT_NUMBER) {
expNumber = 10 * (expNumber) + ch - '0';
}
ch = ++index < endIndex ? str[index] : 0;
ch = charAt(str, ++index, endIndex);
} while (FastDoubleSwar.isDigit(ch));
if (isExponentNegative) {
expNumber = -expNumber;
Expand Down Expand Up @@ -214,7 +214,7 @@ public long parseFloatingPointLiteral(char[] str, int offset, int length) {
// -------------------
final boolean isNegative = ch == '-';
if (isNegative || ch == '+') {
ch = ++index < endIndex ? str[index] : 0;
ch = charAt(str, ++index, endIndex);
if (ch == 0) {
throw new NumberFormatException(SYNTAX_ERROR);
}
Expand All @@ -230,7 +230,7 @@ public long parseFloatingPointLiteral(char[] str, int offset, int length) {
// ---------------------------
final boolean hasLeadingZero = ch == '0';
if (hasLeadingZero) {
ch = ++index < endIndex ? str[index] : 0;
ch = charAt(str, ++index, endIndex);
if (ch == 'x' || ch == 'X') {
return parseHexFloatLiteral(str, index + 1, offset, endIndex, isNegative);
}
Expand Down Expand Up @@ -277,10 +277,10 @@ private long parseHexFloatLiteral(
for (; index < endIndex; index++) {
ch = str[index];
// Table look up is faster than a sequence of if-else-branches.
int hexValue = ch > 127 ? AbstractFloatValueParser.OTHER_CLASS : AbstractFloatValueParser.CHAR_TO_HEX_MAP[ch];
int hexValue = lookupHex(ch);
if (hexValue >= 0) {
significand = (significand << 4) | hexValue;// This might overflow, we deal with it later.
} else if (hexValue == AbstractFloatValueParser.DECIMAL_POINT_CLASS) {
} else if (hexValue == DECIMAL_POINT_CLASS) {
illegal |= virtualIndexOfPoint >= 0;
virtualIndexOfPoint = index;
if (CONDITIONAL_COMPILATION_PARSE_EIGHT_HEX_DIGITS) {
Expand Down Expand Up @@ -312,18 +312,18 @@ private long parseHexFloatLiteral(
int expNumber = 0;
final boolean hasExponent = (ch == 'p') || (ch == 'P');
if (hasExponent) {
ch = ++index < endIndex ? str[index] : 0;
ch = charAt(str, ++index, endIndex);
boolean isExponentNegative = ch == '-';
if (isExponentNegative || ch == '+') {
ch = ++index < endIndex ? str[index] : 0;
ch = charAt(str, ++index, endIndex);
}
illegal |= !FastDoubleSwar.isDigit(ch);
do {
// Guard against overflow
if (expNumber < AbstractFloatValueParser.MAX_EXPONENT_NUMBER) {
expNumber = 10 * (expNumber) + ch - '0';
}
ch = ++index < endIndex ? str[index] : 0;
ch = charAt(str, ++index, endIndex);
} while (FastDoubleSwar.isDigit(ch));
if (isExponentNegative) {
expNumber = -expNumber;
Expand Down Expand Up @@ -356,7 +356,7 @@ private long parseHexFloatLiteral(
for (index = significandStartIndex; index < significandEndIndex; index++) {
ch = str[index];
// Table look up is faster than a sequence of if-else-branches.
int hexValue = ch > 127 ? AbstractFloatValueParser.OTHER_CLASS : AbstractFloatValueParser.CHAR_TO_HEX_MAP[ch];
int hexValue = lookupHex(ch);
if (hexValue >= 0) {
if (Long.compareUnsigned(significand, AbstractFloatValueParser.MINIMAL_NINETEEN_DIGIT_INTEGER) < 0) {
significand = (significand << 4) | hexValue;
Expand Down Expand Up @@ -418,6 +418,7 @@ private long tryToParseEightHexDigits(char[] str, int offset) {
return FastDoubleSwar.tryToParseEightHexDigits(str, offset);
}


/**
* Computes a float value from the given components of a decimal float
* literal.
Expand Down
Loading

0 comments on commit 85a1076

Please sign in to comment.