Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ abstract class AbstractNumberParser {
* to check for byte values {@literal <} 0 before accessing this array.
*/
static final byte[] CHAR_TO_HEX_MAP = new byte[256];
static final short[] CHAR_TO_HEX_MAP2 = new short[256];

static {
Arrays.fill(CHAR_TO_HEX_MAP, OTHER_CLASS);
Expand All @@ -51,6 +52,10 @@ abstract class AbstractNumberParser {
CHAR_TO_HEX_MAP[ch] = (byte) (ch - 'a' + 10);
}
CHAR_TO_HEX_MAP['.'] = DECIMAL_POINT_CLASS;
for (int i = 0; i < 256; i++) {
byte value = CHAR_TO_HEX_MAP[i];
CHAR_TO_HEX_MAP2[i] = value < 0 ? value : (short) (value << 4);
}
}

/**
Expand Down Expand Up @@ -101,10 +106,13 @@ protected static char charAt(CharSequence str, int i, int endIndex) {
* @param ch a character
* @return the hex value or a value &lt; 0.
*/
protected static int lookupHex(byte ch) {
protected static byte lookupHex(byte ch) {
return CHAR_TO_HEX_MAP[ch & 0xff];
}

protected static int lookupHex2(byte high, byte low) {
return CHAR_TO_HEX_MAP2[high & 0xff] | CHAR_TO_HEX_MAP[low & 0xff];
}
/**
* Looks the character up in the {@link #CHAR_TO_HEX_MAP} returns
* a value &lt; 0 if the character is not in the map.
Expand All @@ -116,14 +124,17 @@ protected static int lookupHex(byte ch) {
* @param ch a character
* @return the hex value or a value &lt; 0.
*/
protected static int lookupHex(char ch) {
protected static byte lookupHex(char ch) {
// The branchy code is faster than the branch-less code.
// Branch-less code: return CHAR_TO_HEX_MAP[ch & 0xff] | (127 - ch) >> 31;
// Branch-less code: return CHAR_TO_HEX_MAP[(ch|((127-ch)>>31))&0xff];
// Branch-less code: return CHAR_TO_HEX_MAP[ch<128?ch:0];
return ch < 128 ? CHAR_TO_HEX_MAP[ch] : -1;
}

protected static int lookupHex2(char high, char low) {
return (high | low) < 128 ? CHAR_TO_HEX_MAP2[high] | CHAR_TO_HEX_MAP[low] : -1;
}
/**
* Checks the bounds and returns the end index (exclusive) of the data in the array.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,18 +85,15 @@ private BigInteger parseHexDigits(byte[] str, int from, int to, boolean isNegati

if ((numDigits & 1) != 0) {
byte chLow = str[from++];
int valueLow = lookupHex(chLow);
bytes[index++] = (byte) valueLow;
byte valueLow = lookupHex(chLow);
bytes[index++] = valueLow;
illegalDigits = valueLow < 0;
}
int prerollLimit = from + ((to - from) & 7);
for (; from < prerollLimit; from += 2) {
byte chHigh = str[from];
byte chLow = str[from + 1];
int valueHigh = lookupHex(chHigh);
int valueLow = lookupHex(chLow);
bytes[index++] = (byte) (valueHigh << 4 | valueLow);
illegalDigits |= valueHigh < 0 || valueLow < 0;
int value = lookupHex2(str[from], str[from + 1]);
bytes[index++] = (byte) value;
illegalDigits |= value < 0;
}
for (; from < to; from += 8, index += 4) {
long value = FastDoubleSwar.tryToParseEightHexDigits(str, from);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,18 +81,15 @@ private BigInteger parseHexDigits(char[] str, int from, int to, boolean isNegati

if ((numDigits & 1) != 0) {
char chLow = str[from++];
int valueLow = lookupHex(chLow);
bytes[index++] = (byte) valueLow;
byte valueLow = lookupHex(chLow);
bytes[index++] = valueLow;
illegalDigits = valueLow < 0;
}
int prerollLimit = from + ((to - from) & 7);
for (; from < prerollLimit; from += 2) {
char chHigh = str[from];
char chLow = str[from + 1];
int valueHigh = lookupHex(chHigh);
int valueLow = lookupHex(chLow);
bytes[index++] = (byte) (valueHigh << 4 | valueLow);
illegalDigits |= valueHigh < 0 || valueLow < 0;
int value = lookupHex2(str[from], str[from + 1]);
bytes[index++] = (byte) value;
illegalDigits |= value < 0;
}
for (; from < to; from += 8, index += 4) {
long value = FastDoubleSwar.tryToParseEightHexDigits(str, from);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,18 +82,15 @@ private BigInteger parseHexDigits(CharSequence str, int from, int to, boolean is
boolean illegalDigits = false;
if ((numDigits & 1) != 0) {
char chLow = str.charAt(from++);
int valueLow = lookupHex(chLow);
bytes[index++] = (byte) valueLow;
byte valueLow = lookupHex(chLow);
bytes[index++] = valueLow;
illegalDigits = valueLow < 0;
}
int prerollLimit = from + ((to - from) & 7);
for (; from < prerollLimit; from += 2) {
char chHigh = str.charAt(from);
char chLow = str.charAt(from + 1);
int valueHigh = lookupHex(chHigh);
int valueLow = lookupHex(chLow);
bytes[index++] = (byte) (valueHigh << 4 | valueLow);
illegalDigits |= valueLow < 0 || valueHigh < 0;
int value = lookupHex2(str.charAt(from), str.charAt(from + 1));
bytes[index++] = (byte) value;
illegalDigits |= value < 0;
}
for (; from < to; from += 8, index += 4) {
long value = FastDoubleSwar.tryToParseEightHexDigits(str, from);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
/*
* @(#)JmhFloat.java
* Copyright © 2023 Werner Randelshofer, Switzerland. MIT License.
*/
package ch.randelshofer.fastdoubleparser;

import org.openjdk.jmh.annotations.*;
import org.openjdk.jmh.infra.Blackhole;

import java.util.concurrent.TimeUnit;

/**
* Benchmarks for selected floating point strings.
* <pre>
* # JMH version: 1.36
* # JDK 20.0.1, OpenJDK 64-Bit Server VM, 20.0.1+9-29
* # Intel(R) Core(TM) i5-6200U CPU @ 2.30GHz
*
* Benchmark (str) Mode Cnt Score Error Units
* lookupHex2Byte avgt 5 2.020 ± 0.009 ns/op
* lookupHex2Char avgt 5 2.019 ± 0.010 ns/op
* lookupHexTwiceByte avgt 5 2.336 ± 0.057 ns/op
* lookupHexTwiceChar avgt 5 2.321 ± 0.075 ns/op
*
* Process finished with exit code 0
* </pre>
*/

@Fork(value = 1)
@Measurement(iterations = 5, time = 1)
@Warmup(iterations = 2, time = 1)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@BenchmarkMode(Mode.AverageTime)
@State(Scope.Benchmark)
public class JmhLookupHex2 {

public char highChar = 'A';
public char lowChar = '1';
public char highByte = 'A';
public char lowByte = '1';
public boolean illegalDigits;

@Setup(Level.Iteration)
public void prepare(){
illegalDigits = false;// otherwise it seems that evaluation of expression for "illegalDigits" is skipped
}

// several lines of code added to imitate the context, which optimized method is situated in
@Benchmark
public void lookupHex2Char(Blackhole blackhole) {
int result = AbstractNumberParser.lookupHex2(highChar, lowChar);
byte b = (byte) result;
blackhole.consume(b);
illegalDigits |= result < 0;
blackhole.consume(illegalDigits);
}

@Benchmark
public void lookupHexTwiceChar(Blackhole blackhole) {
int high = AbstractNumberParser.lookupHex(highChar);
int low = AbstractNumberParser.lookupHex(lowChar);
byte b = (byte) (high << 4 | low);
blackhole.consume(b);
illegalDigits |= low < 0 | high < 0;
blackhole.consume(illegalDigits);
}

@Benchmark
public void lookupHex2Byte(Blackhole blackhole) {
int result = AbstractNumberParser.lookupHex2(highByte, lowByte);
byte b = (byte) result;
blackhole.consume(b);
illegalDigits |= result < 0;
blackhole.consume(illegalDigits);
}

@Benchmark
public void lookupHexTwiceByte(Blackhole blackhole) {
int high = AbstractNumberParser.lookupHex(highByte);
int low = AbstractNumberParser.lookupHex(lowByte);
byte b = (byte) (high << 4 | low);
blackhole.consume(b);
illegalDigits |= low < 0 | high < 0;
blackhole.consume(illegalDigits);
}
}