From 91db7c0877a68ad171da2b4501280fc24630ae83 Mon Sep 17 00:00:00 2001 From: Shaojin Wen Date: Tue, 7 Jan 2025 13:55:30 +0800 Subject: [PATCH 01/14] add benchmark --- test/micro/org/openjdk/bench/java/lang/Integers.java | 7 +++++++ test/micro/org/openjdk/bench/java/lang/Longs.java | 8 ++++++++ 2 files changed, 15 insertions(+) diff --git a/test/micro/org/openjdk/bench/java/lang/Integers.java b/test/micro/org/openjdk/bench/java/lang/Integers.java index 43ceb5d18d207..9bf0be06d3dbb 100644 --- a/test/micro/org/openjdk/bench/java/lang/Integers.java +++ b/test/micro/org/openjdk/bench/java/lang/Integers.java @@ -106,6 +106,13 @@ public void toStringTiny(Blackhole bh) { } } + @Benchmark + public void toHexString(Blackhole bh) { + for (int i : intsBig) { + bh.consume(Integer.toHexString(i)); + } + } + /** Performs toString on large values, roughly 10 digits. */ @Benchmark public void toStringBig(Blackhole bh) { diff --git a/test/micro/org/openjdk/bench/java/lang/Longs.java b/test/micro/org/openjdk/bench/java/lang/Longs.java index 765d00e9fb90e..1e64b77ba9099 100644 --- a/test/micro/org/openjdk/bench/java/lang/Longs.java +++ b/test/micro/org/openjdk/bench/java/lang/Longs.java @@ -85,6 +85,14 @@ public void decode(Blackhole bh) { } } + /** Performs toString on large values, around 10 digits. */ + @Benchmark + public void toHexString(Blackhole bh) { + for (long value : longArrayBig) { + bh.consume(Long.toHexString(value)); + } + } + /** Performs toString on large values, around 10 digits. */ @Benchmark public void toStringBig(Blackhole bh) { From 79fe74eb8f74d21c16cb2850a55cf8a37d9b3f65 Mon Sep 17 00:00:00 2001 From: Shaojin Wen Date: Tue, 7 Jan 2025 13:56:26 +0800 Subject: [PATCH 02/14] optimize toHexString --- .../share/classes/java/lang/Integer.java | 50 ++++++++++++++++++- .../share/classes/java/lang/Long.java | 33 +++++++++++- 2 files changed, 81 insertions(+), 2 deletions(-) diff --git a/src/java.base/share/classes/java/lang/Integer.java b/src/java.base/share/classes/java/lang/Integer.java index a6bf739220fc3..3f8efd35430fc 100644 --- a/src/java.base/share/classes/java/lang/Integer.java +++ b/src/java.base/share/classes/java/lang/Integer.java @@ -286,7 +286,55 @@ public static String toUnsignedString(int i, int radix) { * @since 1.0.2 */ public static String toHexString(int i) { - return toUnsignedString0(i, 4); + int mag = Integer.SIZE - Integer.numberOfLeadingZeros(i); + int len = Math.max(((mag + 3) / 4), 1); + byte coder = COMPACT_STRINGS ? LATIN1 : UTF16; + byte[] chars = new byte[len << coder]; + long x = hex8(i); + do { + --len; + byte b = (byte) x; + if (COMPACT_STRINGS) { + StringLatin1.putChar(chars, len, b); + } else { + StringUTF16.putChar(chars, len, b); + } + x >>>= 8; + } while (len > 0); + return new String(chars, coder); + } + + /** + * Extract the least significant 8 bytes from the input integer i, convert each byte into its corresponding 2-digit + * hexadecimal representation, concatenate these hexadecimal strings into one continuous string, and then interpret + * this string as a hexadecimal number to form and return a long value. + */ + static long hex8(long i) { + long x = Long.expand(i, 0x0F0F_0F0F_0F0F_0F0FL); + /* + Use long to simulate vector operations and generate 8 hexadecimal characters at a time. + ------------ + 0 = 0b0000_0000 => m = ((i + 6) & 0x10); (m << 1) + (m >> 1) - (m >> 4) => 0 + 0x30 + (i & 0xF) => '0' + 1 = 0b0000_0001 => m = ((i + 6) & 0x10); (m << 1) + (m >> 1) - (m >> 4) => 0 + 0x30 + (i & 0xF) => '1' + 2 = 0b0000_0010 => m = ((i + 6) & 0x10); (m << 1) + (m >> 1) - (m >> 4) => 0 + 0x30 + (i & 0xF) => '2' + 3 = 0b0000_0011 => m = ((i + 6) & 0x10); (m << 1) + (m >> 1) - (m >> 4) => 0 + 0x30 + (i & 0xF) => '3' + 4 = 0b0000_0100 => m = ((i + 6) & 0x10); (m << 1) + (m >> 1) - (m >> 4) => 0 + 0x30 + (i & 0xF) => '4' + 5 = 0b0000_0101 => m = ((i + 6) & 0x10); (m << 1) + (m >> 1) - (m >> 4) => 0 + 0x30 + (i & 0xF) => '5' + 6 = 0b0000_0110 => m = ((i + 6) & 0x10); (m << 1) + (m >> 1) - (m >> 4) => 0 + 0x30 + (i & 0xF) => '6' + 7 = 0b0000_0111 => m = ((i + 6) & 0x10); (m << 1) + (m >> 1) - (m >> 4) => 0 + 0x30 + (i & 0xF) => '7' + 8 = 0b0000_1000 => m = ((i + 6) & 0x10); (m << 1) + (m >> 1) - (m >> 4) => 0 + 0x30 + (i & 0xF) => '8' + 9 = 0b0000_1001 => m = ((i + 6) & 0x10); (m << 1) + (m >> 1) - (m >> 4) => 0 + 0x30 + (i & 0xF) => '9' + 10 = 0b0000_1010 => m = ((i + 6) & 0x10); (m << 1) + (m >> 1) - (m >> 4) => 39 + 0x30 + (i & 0xF) => 'a' + 11 = 0b0000_1011 => m = ((i + 6) & 0x10); (m << 1) + (m >> 1) - (m >> 4) => 39 + 0x30 + (i & 0xF) => 'b' + 12 = 0b0000_1100 => m = ((i + 6) & 0x10); (m << 1) + (m >> 1) - (m >> 4) => 39 + 0x30 + (i & 0xF) => 'c' + 13 = 0b0000_1101 => m = ((i + 6) & 0x10); (m << 1) + (m >> 1) - (m >> 4) => 39 + 0x30 + (i & 0xF) => 'd' + 14 = 0b0000_1110 => m = ((i + 6) & 0x10); (m << 1) + (m >> 1) - (m >> 4) => 39 + 0x30 + (i & 0xF) => 'e' + 15 = 0b0000_1111 => m = ((i + 6) & 0x10); (m << 1) + (m >> 1) - (m >> 4) => 39 + 0x30 + (i & 0xF) => 'f' + */ + long m = (x + 0x0606_0606_0606_0606L) & 0x1010_1010_1010_1010L; + return ((m << 1) + (m >> 1) - (m >> 4)) + + 0x3030_3030_3030_3030L + + (x & 0x0F0F_0F0F_0F0F_0F0FL); } /** diff --git a/src/java.base/share/classes/java/lang/Long.java b/src/java.base/share/classes/java/lang/Long.java index 822199bb09b51..deea79ef5d70b 100644 --- a/src/java.base/share/classes/java/lang/Long.java +++ b/src/java.base/share/classes/java/lang/Long.java @@ -34,6 +34,7 @@ import java.util.Optional; import jdk.internal.misc.CDS; +import jdk.internal.misc.Unsafe; import jdk.internal.util.DecimalDigits; import jdk.internal.vm.annotation.ForceInline; import jdk.internal.vm.annotation.IntrinsicCandidate; @@ -43,6 +44,7 @@ import static java.lang.String.COMPACT_STRINGS; import static java.lang.String.LATIN1; import static java.lang.String.UTF16; +import static jdk.internal.misc.Unsafe.ARRAY_BYTE_BASE_OFFSET; /** * The {@code Long} class is the {@linkplain @@ -311,7 +313,36 @@ private static BigInteger toUnsignedBigInteger(long i) { * @since 1.0.2 */ public static String toHexString(long i) { - return toUnsignedString0(i, 4); + int mag = Long.SIZE - Long.numberOfLeadingZeros(i); + int len = Math.max(((mag + 3) / 4), 1); + byte coder = COMPACT_STRINGS ? LATIN1 : UTF16; + byte[] chars = new byte[len << coder]; + byte b; + long x = Integer.hex8(i); + if (len > 8) { + if (COMPACT_STRINGS) { + len -= 8; + Unsafe.getUnsafe().putLong(chars, ARRAY_BYTE_BASE_OFFSET + len, Long.reverseBytes(x)); + } else { + for (int j = 0; j < 8; j++) { + b = (byte) x; + StringUTF16.putChar(chars, --len, b); + x >>>= 8; + } + } + x = Integer.hex8(i >>> 32); + } + do { + --len; + b = (byte) x; + if (COMPACT_STRINGS) { + chars[len] = b; + } else { + StringUTF16.putChar(chars, len, b); + } + x >>>= 8; + } while (len > 0); + return new String(chars, coder); } /** From 1788d09787cadfe6ec23b9b10bef87a2cdc029a3 Mon Sep 17 00:00:00 2001 From: Shaojin Wen Date: Wed, 8 Jan 2025 03:41:04 +0800 Subject: [PATCH 03/14] refactor --- .../share/classes/java/lang/Integer.java | 64 +++++-------------- .../share/classes/java/lang/Long.java | 43 +++++++------ .../classes/jdk/internal/util/HexDigits.java | 33 ++++++++++ 3 files changed, 73 insertions(+), 67 deletions(-) diff --git a/src/java.base/share/classes/java/lang/Integer.java b/src/java.base/share/classes/java/lang/Integer.java index 3f8efd35430fc..b41ffdff770cc 100644 --- a/src/java.base/share/classes/java/lang/Integer.java +++ b/src/java.base/share/classes/java/lang/Integer.java @@ -28,6 +28,7 @@ import jdk.internal.misc.CDS; import jdk.internal.misc.VM; import jdk.internal.util.DecimalDigits; +import jdk.internal.util.HexDigits; import jdk.internal.vm.annotation.ForceInline; import jdk.internal.vm.annotation.IntrinsicCandidate; import jdk.internal.vm.annotation.Stable; @@ -288,53 +289,22 @@ public static String toUnsignedString(int i, int radix) { public static String toHexString(int i) { int mag = Integer.SIZE - Integer.numberOfLeadingZeros(i); int len = Math.max(((mag + 3) / 4), 1); - byte coder = COMPACT_STRINGS ? LATIN1 : UTF16; - byte[] chars = new byte[len << coder]; - long x = hex8(i); - do { - --len; - byte b = (byte) x; - if (COMPACT_STRINGS) { - StringLatin1.putChar(chars, len, b); - } else { - StringUTF16.putChar(chars, len, b); - } - x >>>= 8; - } while (len > 0); - return new String(chars, coder); - } - - /** - * Extract the least significant 8 bytes from the input integer i, convert each byte into its corresponding 2-digit - * hexadecimal representation, concatenate these hexadecimal strings into one continuous string, and then interpret - * this string as a hexadecimal number to form and return a long value. - */ - static long hex8(long i) { - long x = Long.expand(i, 0x0F0F_0F0F_0F0F_0F0FL); - /* - Use long to simulate vector operations and generate 8 hexadecimal characters at a time. - ------------ - 0 = 0b0000_0000 => m = ((i + 6) & 0x10); (m << 1) + (m >> 1) - (m >> 4) => 0 + 0x30 + (i & 0xF) => '0' - 1 = 0b0000_0001 => m = ((i + 6) & 0x10); (m << 1) + (m >> 1) - (m >> 4) => 0 + 0x30 + (i & 0xF) => '1' - 2 = 0b0000_0010 => m = ((i + 6) & 0x10); (m << 1) + (m >> 1) - (m >> 4) => 0 + 0x30 + (i & 0xF) => '2' - 3 = 0b0000_0011 => m = ((i + 6) & 0x10); (m << 1) + (m >> 1) - (m >> 4) => 0 + 0x30 + (i & 0xF) => '3' - 4 = 0b0000_0100 => m = ((i + 6) & 0x10); (m << 1) + (m >> 1) - (m >> 4) => 0 + 0x30 + (i & 0xF) => '4' - 5 = 0b0000_0101 => m = ((i + 6) & 0x10); (m << 1) + (m >> 1) - (m >> 4) => 0 + 0x30 + (i & 0xF) => '5' - 6 = 0b0000_0110 => m = ((i + 6) & 0x10); (m << 1) + (m >> 1) - (m >> 4) => 0 + 0x30 + (i & 0xF) => '6' - 7 = 0b0000_0111 => m = ((i + 6) & 0x10); (m << 1) + (m >> 1) - (m >> 4) => 0 + 0x30 + (i & 0xF) => '7' - 8 = 0b0000_1000 => m = ((i + 6) & 0x10); (m << 1) + (m >> 1) - (m >> 4) => 0 + 0x30 + (i & 0xF) => '8' - 9 = 0b0000_1001 => m = ((i + 6) & 0x10); (m << 1) + (m >> 1) - (m >> 4) => 0 + 0x30 + (i & 0xF) => '9' - 10 = 0b0000_1010 => m = ((i + 6) & 0x10); (m << 1) + (m >> 1) - (m >> 4) => 39 + 0x30 + (i & 0xF) => 'a' - 11 = 0b0000_1011 => m = ((i + 6) & 0x10); (m << 1) + (m >> 1) - (m >> 4) => 39 + 0x30 + (i & 0xF) => 'b' - 12 = 0b0000_1100 => m = ((i + 6) & 0x10); (m << 1) + (m >> 1) - (m >> 4) => 39 + 0x30 + (i & 0xF) => 'c' - 13 = 0b0000_1101 => m = ((i + 6) & 0x10); (m << 1) + (m >> 1) - (m >> 4) => 39 + 0x30 + (i & 0xF) => 'd' - 14 = 0b0000_1110 => m = ((i + 6) & 0x10); (m << 1) + (m >> 1) - (m >> 4) => 39 + 0x30 + (i & 0xF) => 'e' - 15 = 0b0000_1111 => m = ((i + 6) & 0x10); (m << 1) + (m >> 1) - (m >> 4) => 39 + 0x30 + (i & 0xF) => 'f' - */ - long m = (x + 0x0606_0606_0606_0606L) & 0x1010_1010_1010_1010L; - return ((m << 1) + (m >> 1) - (m >> 4)) - + 0x3030_3030_3030_3030L - + (x & 0x0F0F_0F0F_0F0F_0F0FL); + long x = HexDigits.hex8(i); + if (COMPACT_STRINGS) { + byte[] chars = new byte[len]; + do { + chars[--len] = (byte) x; + x >>>= 8; + } while (len > 0); + return new String(chars, LATIN1); + } else { + byte[] chars = new byte[len << 1]; + do { + StringUTF16.putChar(chars, --len, (byte) x); + x >>>= 8; + } while (len > 0); + return new String(chars, UTF16); + } } /** diff --git a/src/java.base/share/classes/java/lang/Long.java b/src/java.base/share/classes/java/lang/Long.java index deea79ef5d70b..88f7018464f85 100644 --- a/src/java.base/share/classes/java/lang/Long.java +++ b/src/java.base/share/classes/java/lang/Long.java @@ -36,6 +36,7 @@ import jdk.internal.misc.CDS; import jdk.internal.misc.Unsafe; import jdk.internal.util.DecimalDigits; +import jdk.internal.util.HexDigits; import jdk.internal.vm.annotation.ForceInline; import jdk.internal.vm.annotation.IntrinsicCandidate; import jdk.internal.vm.annotation.Stable; @@ -315,34 +316,36 @@ private static BigInteger toUnsignedBigInteger(long i) { public static String toHexString(long i) { int mag = Long.SIZE - Long.numberOfLeadingZeros(i); int len = Math.max(((mag + 3) / 4), 1); - byte coder = COMPACT_STRINGS ? LATIN1 : UTF16; - byte[] chars = new byte[len << coder]; - byte b; - long x = Integer.hex8(i); - if (len > 8) { - if (COMPACT_STRINGS) { + long x = HexDigits.hex8(i); + if (COMPACT_STRINGS) { + byte[] chars = new byte[len]; + if (len > 8) { len -= 8; - Unsafe.getUnsafe().putLong(chars, ARRAY_BYTE_BASE_OFFSET + len, Long.reverseBytes(x)); - } else { + Unsafe.getUnsafe().putLongUnaligned(chars, ARRAY_BYTE_BASE_OFFSET + len, x, true); + x = HexDigits.hex8(i >>> 32); + } + do { + chars[--len] = (byte) x; + x >>>= 8; + } while (len > 0); + return new String(chars, LATIN1); + } else { + byte[] chars = new byte[len << 1]; + byte b; + if (len > 8) { for (int j = 0; j < 8; j++) { b = (byte) x; StringUTF16.putChar(chars, --len, b); x >>>= 8; } + x = HexDigits.hex8(i >>> 32); } - x = Integer.hex8(i >>> 32); + do { + StringUTF16.putChar(chars, --len, (byte) x); + x >>>= 8; + } while (len > 0); + return new String(chars, UTF16); } - do { - --len; - b = (byte) x; - if (COMPACT_STRINGS) { - chars[len] = b; - } else { - StringUTF16.putChar(chars, len, b); - } - x >>>= 8; - } while (len > 0); - return new String(chars, coder); } /** diff --git a/src/java.base/share/classes/jdk/internal/util/HexDigits.java b/src/java.base/share/classes/jdk/internal/util/HexDigits.java index c08db4f5b48fd..0c387ee9f0c33 100644 --- a/src/java.base/share/classes/jdk/internal/util/HexDigits.java +++ b/src/java.base/share/classes/jdk/internal/util/HexDigits.java @@ -194,4 +194,37 @@ public static int stringSize(long value) { return value == 0 ? 1 : 67 - Long.numberOfLeadingZeros(value) >> 2; } + + /** + * Extract the least significant 8 bytes from the input integer i, convert each byte into its corresponding 2-digit + * hexadecimal representation, concatenate these hexadecimal strings into one continuous string, and then interpret + * this string as a hexadecimal number to form and return a long value. + */ + public static long hex8(long i) { + long x = Long.expand(i, 0x0F0F_0F0F_0F0F_0F0FL); + /* + Use long to simulate vector operations and generate 8 hexadecimal characters at a time. + ------------ + 0 = 0b0000_0000 => m = ((i + 6) & 0x10); (m << 1) + (m >> 1) - (m >> 4) => 0 + 0x30 + (i & 0xF) => '0' + 1 = 0b0000_0001 => m = ((i + 6) & 0x10); (m << 1) + (m >> 1) - (m >> 4) => 0 + 0x30 + (i & 0xF) => '1' + 2 = 0b0000_0010 => m = ((i + 6) & 0x10); (m << 1) + (m >> 1) - (m >> 4) => 0 + 0x30 + (i & 0xF) => '2' + 3 = 0b0000_0011 => m = ((i + 6) & 0x10); (m << 1) + (m >> 1) - (m >> 4) => 0 + 0x30 + (i & 0xF) => '3' + 4 = 0b0000_0100 => m = ((i + 6) & 0x10); (m << 1) + (m >> 1) - (m >> 4) => 0 + 0x30 + (i & 0xF) => '4' + 5 = 0b0000_0101 => m = ((i + 6) & 0x10); (m << 1) + (m >> 1) - (m >> 4) => 0 + 0x30 + (i & 0xF) => '5' + 6 = 0b0000_0110 => m = ((i + 6) & 0x10); (m << 1) + (m >> 1) - (m >> 4) => 0 + 0x30 + (i & 0xF) => '6' + 7 = 0b0000_0111 => m = ((i + 6) & 0x10); (m << 1) + (m >> 1) - (m >> 4) => 0 + 0x30 + (i & 0xF) => '7' + 8 = 0b0000_1000 => m = ((i + 6) & 0x10); (m << 1) + (m >> 1) - (m >> 4) => 0 + 0x30 + (i & 0xF) => '8' + 9 = 0b0000_1001 => m = ((i + 6) & 0x10); (m << 1) + (m >> 1) - (m >> 4) => 0 + 0x30 + (i & 0xF) => '9' + 10 = 0b0000_1010 => m = ((i + 6) & 0x10); (m << 1) + (m >> 1) - (m >> 4) => 39 + 0x30 + (i & 0xF) => 'a' + 11 = 0b0000_1011 => m = ((i + 6) & 0x10); (m << 1) + (m >> 1) - (m >> 4) => 39 + 0x30 + (i & 0xF) => 'b' + 12 = 0b0000_1100 => m = ((i + 6) & 0x10); (m << 1) + (m >> 1) - (m >> 4) => 39 + 0x30 + (i & 0xF) => 'c' + 13 = 0b0000_1101 => m = ((i + 6) & 0x10); (m << 1) + (m >> 1) - (m >> 4) => 39 + 0x30 + (i & 0xF) => 'd' + 14 = 0b0000_1110 => m = ((i + 6) & 0x10); (m << 1) + (m >> 1) - (m >> 4) => 39 + 0x30 + (i & 0xF) => 'e' + 15 = 0b0000_1111 => m = ((i + 6) & 0x10); (m << 1) + (m >> 1) - (m >> 4) => 39 + 0x30 + (i & 0xF) => 'f' + */ + long m = (x + 0x0606_0606_0606_0606L) & 0x1010_1010_1010_1010L; + return ((m << 1) + (m >> 1) - (m >> 4)) + + 0x3030_3030_3030_3030L + + (x & 0x0F0F_0F0F_0F0F_0F0FL); + } } From 967694f69ec900e22c24c1c998998633b7bf9e2a Mon Sep 17 00:00:00 2001 From: Shaojin Wen Date: Wed, 8 Jan 2025 07:33:16 +0800 Subject: [PATCH 04/14] fix comment --- src/java.base/share/classes/jdk/internal/util/HexDigits.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/java.base/share/classes/jdk/internal/util/HexDigits.java b/src/java.base/share/classes/jdk/internal/util/HexDigits.java index 0c387ee9f0c33..dcdaec570fffa 100644 --- a/src/java.base/share/classes/jdk/internal/util/HexDigits.java +++ b/src/java.base/share/classes/jdk/internal/util/HexDigits.java @@ -196,7 +196,7 @@ public static int stringSize(long value) { } /** - * Extract the least significant 8 bytes from the input integer i, convert each byte into its corresponding 2-digit + * Extract the least significant 4 bytes from the input integer i, convert each byte into its corresponding 2-digit * hexadecimal representation, concatenate these hexadecimal strings into one continuous string, and then interpret * this string as a hexadecimal number to form and return a long value. */ From 8aae88ee06fb1975bd72a9834e2ba381ae549604 Mon Sep 17 00:00:00 2001 From: Shaojin Wen Date: Wed, 8 Jan 2025 08:23:21 +0800 Subject: [PATCH 05/14] add benchmark --- .../org/openjdk/bench/java/lang/Integers.java | 24 +++++++++++++++---- .../org/openjdk/bench/java/lang/Longs.java | 14 +++++++---- 2 files changed, 29 insertions(+), 9 deletions(-) diff --git a/test/micro/org/openjdk/bench/java/lang/Integers.java b/test/micro/org/openjdk/bench/java/lang/Integers.java index 9bf0be06d3dbb..404122cfbaa37 100644 --- a/test/micro/org/openjdk/bench/java/lang/Integers.java +++ b/test/micro/org/openjdk/bench/java/lang/Integers.java @@ -44,7 +44,7 @@ @BenchmarkMode(Mode.AverageTime) @OutputTimeUnit(TimeUnit.MICROSECONDS) @State(Scope.Thread) -@Warmup(iterations = 10, time = 500, timeUnit = TimeUnit.MILLISECONDS) +@Warmup(iterations = 5, time = 500, timeUnit = TimeUnit.MILLISECONDS) @Measurement(iterations = 5, time = 1000, timeUnit = TimeUnit.MILLISECONDS) @Fork(3) public class Integers { @@ -106,18 +106,32 @@ public void toStringTiny(Blackhole bh) { } } + /** Performs toString on large values, roughly 10 digits. */ @Benchmark - public void toHexString(Blackhole bh) { + public void toStringBig(Blackhole bh) { for (int i : intsBig) { + bh.consume(Integer.toString(i)); + } + } + + @Benchmark + public void toHexStringTiny(Blackhole bh) { + for (int i : intsTiny) { bh.consume(Integer.toHexString(i)); } } - /** Performs toString on large values, roughly 10 digits. */ @Benchmark - public void toStringBig(Blackhole bh) { + public void toHexStringSmall(Blackhole bh) { + for (int i : intsSmall) { + bh.consume(Integer.toHexString(i)); + } + } + + @Benchmark + public void toHexStringBig(Blackhole bh) { for (int i : intsBig) { - bh.consume(Integer.toString(i)); + bh.consume(Integer.toHexString(i)); } } diff --git a/test/micro/org/openjdk/bench/java/lang/Longs.java b/test/micro/org/openjdk/bench/java/lang/Longs.java index 1e64b77ba9099..dac6c1b4a401c 100644 --- a/test/micro/org/openjdk/bench/java/lang/Longs.java +++ b/test/micro/org/openjdk/bench/java/lang/Longs.java @@ -87,17 +87,23 @@ public void decode(Blackhole bh) { /** Performs toString on large values, around 10 digits. */ @Benchmark - public void toHexString(Blackhole bh) { + public void toStringBig(Blackhole bh) { for (long value : longArrayBig) { + bh.consume(Long.toString(value)); + } + } + + @Benchmark + public void toHexStringSmall(Blackhole bh) { + for (long value : longArraySmall) { bh.consume(Long.toHexString(value)); } } - /** Performs toString on large values, around 10 digits. */ @Benchmark - public void toStringBig(Blackhole bh) { + public void toHexStringBig(Blackhole bh) { for (long value : longArrayBig) { - bh.consume(Long.toString(value)); + bh.consume(Long.toHexString(value)); } } From 6704531b8475a2d5f680521511a72fdcfa0ef461 Mon Sep 17 00:00:00 2001 From: Shaojin Wen Date: Wed, 8 Jan 2025 13:24:40 +0800 Subject: [PATCH 06/14] Remove unnecessary steps --- src/java.base/share/classes/jdk/internal/util/HexDigits.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/java.base/share/classes/jdk/internal/util/HexDigits.java b/src/java.base/share/classes/jdk/internal/util/HexDigits.java index dcdaec570fffa..d611dddab6be3 100644 --- a/src/java.base/share/classes/jdk/internal/util/HexDigits.java +++ b/src/java.base/share/classes/jdk/internal/util/HexDigits.java @@ -225,6 +225,6 @@ public static long hex8(long i) { long m = (x + 0x0606_0606_0606_0606L) & 0x1010_1010_1010_1010L; return ((m << 1) + (m >> 1) - (m >> 4)) + 0x3030_3030_3030_3030L - + (x & 0x0F0F_0F0F_0F0F_0F0FL); + + x; } } From ca0433d7f421fee1bd5ba1b22fe718da048a0dc8 Mon Sep 17 00:00:00 2001 From: Shaojin Wen Date: Fri, 23 May 2025 08:38:49 +0800 Subject: [PATCH 07/14] use HexDigits hex8 --- .../share/classes/java/util/UUID.java | 86 +-------------- .../classes/jdk/internal/util/HexDigits.java | 100 +++++++++++++----- 2 files changed, 78 insertions(+), 108 deletions(-) diff --git a/src/java.base/share/classes/java/util/UUID.java b/src/java.base/share/classes/java/util/UUID.java index 5961fce9cb23c..bcb133744787c 100644 --- a/src/java.base/share/classes/java/util/UUID.java +++ b/src/java.base/share/classes/java/util/UUID.java @@ -32,6 +32,7 @@ import jdk.internal.access.JavaLangAccess; import jdk.internal.access.SharedSecrets; import jdk.internal.util.ByteArrayLittleEndian; +import jdk.internal.util.HexDigits; /** * A class that represents an immutable universally unique identifier (UUID). @@ -469,15 +470,15 @@ public String toString() { // Although the UUID byte ordering is defined to be big-endian, ByteArrayLittleEndian is used here to optimize // for the most common architectures. hex8 reverses the order internally. - ByteArrayLittleEndian.setLong(buf, 0, hex8(mostSigBits >>> 32)); - long x0 = hex8(mostSigBits); + ByteArrayLittleEndian.setLong(buf, 0, HexDigits.hex8(mostSigBits >>> 32)); + long x0 = HexDigits.hex8(mostSigBits); ByteArrayLittleEndian.setInt(buf, 9, (int) x0); ByteArrayLittleEndian.setInt(buf, 14, (int) (x0 >>> 32)); - long x1 = hex8(leastSigBits >>> 32); + long x1 = HexDigits.hex8(leastSigBits >>> 32); ByteArrayLittleEndian.setInt(buf, 19, (int) (x1)); ByteArrayLittleEndian.setInt(buf, 24, (int) (x1 >>> 32)); - ByteArrayLittleEndian.setLong(buf, 28, hex8(leastSigBits)); + ByteArrayLittleEndian.setLong(buf, 28, HexDigits.hex8(leastSigBits)); try { return jla.uncheckedNewStringNoRepl(buf, StandardCharsets.ISO_8859_1); @@ -486,83 +487,6 @@ public String toString() { } } - /** - * Efficiently converts 8 hexadecimal digits to their ASCII representation using SIMD-style vector operations. - * This method processes multiple digits in parallel by treating a long value as eight 8-bit lanes, - * achieving significantly better performance compared to traditional loop-based conversion. - * - *

The conversion algorithm works as follows: - *

-     * 1. Input expansion: Each 4-bit hex digit is expanded to 8 bits
-     * 2. Vector processing:
-     *    - Add 6 to each digit: triggers carry flag for a-f digits
-     *    - Mask with 0x10 pattern to isolate carry flags
-     *    - Calculate ASCII adjustment: (carry << 1) + (carry >> 1) - (carry >> 4)
-     *    - Add ASCII '0' base (0x30) and original value
-     * 3. Byte order adjustment for final output
-     * 
- * - *

Performance characteristics: - *

    - *
  • Processes 8 digits in parallel using vector operations - *
  • Avoids branching and loops completely - *
  • Uses only integer arithmetic and bit operations - *
  • Constant time execution regardless of input values - *
- * - *

ASCII conversion mapping: - *

    - *
  • Digits 0-9 → ASCII '0'-'9' (0x30-0x39) - *
  • Digits a-f → ASCII 'a'-'f' (0x61-0x66) - *
- * - * @param input A long containing 8 hex digits (each digit must be 0-15) - * @return A long containing 8 ASCII bytes representing the hex digits - * - * @implNote The implementation leverages CPU vector processing capabilities through - * long integer operations. The algorithm is based on the observation that - * ASCII hex digits have a specific pattern that can be computed efficiently - * using carry flag manipulation. - * - * @example - *
-     * Input:  0xABCDEF01
-     * Output: 3130666564636261 ('1','0','f','e','d','c','b','a' in ASCII)
-     * 
- * - * @see Long#reverseBytes(long) - */ - private static long hex8(long i) { - // Expand each 4-bit group into 8 bits, spreading them out in the long value: 0xAABBCCDD -> 0xA0A0B0B0C0C0D0D - i = Long.expand(i, 0x0F0F_0F0F_0F0F_0F0FL); - - /* - * This method efficiently converts 8 hexadecimal digits simultaneously using vector operations - * The algorithm works as follows: - * - * For input values 0-15: - * - For digits 0-9: converts to ASCII '0'-'9' (0x30-0x39) - * - For digits 10-15: converts to ASCII 'a'-'f' (0x61-0x66) - * - * The conversion process: - * 1. Add 6 to each 4-bit group: i + 0x0606_0606_0606_0606L - * 2. Mask to get the adjustment flags: & 0x1010_1010_1010_1010L - * 3. Calculate the offset: (m << 1) + (m >> 1) - (m >> 4) - * - For 0-9: offset = 0 - * - For a-f: offset = 39 (to bridge the gap between '9' and 'a' in ASCII) - * 4. Add ASCII '0' base (0x30) and the original value - * 5. Reverse byte order for correct positioning - */ - long m = (i + 0x0606_0606_0606_0606L) & 0x1010_1010_1010_1010L; - - // Calculate final ASCII values and reverse bytes for proper ordering - return Long.reverseBytes( - ((m << 1) + (m >> 1) - (m >> 4)) - + 0x3030_3030_3030_3030L // Add ASCII '0' base to all digits - + i // Add original values - ); - } - /** * Returns a hash code for this {@code UUID}. * diff --git a/src/java.base/share/classes/jdk/internal/util/HexDigits.java b/src/java.base/share/classes/jdk/internal/util/HexDigits.java index 1f73b42811fc6..61d9ca0986714 100644 --- a/src/java.base/share/classes/jdk/internal/util/HexDigits.java +++ b/src/java.base/share/classes/jdk/internal/util/HexDigits.java @@ -179,36 +179,82 @@ public static int stringSize(long value) { 67 - Long.numberOfLeadingZeros(value) >> 2; } + + /** - * Extract the least significant 4 bytes from the input integer i, convert each byte into its corresponding 2-digit - * hexadecimal representation, concatenate these hexadecimal strings into one continuous string, and then interpret - * this string as a hexadecimal number to form and return a long value. + * Efficiently converts 8 hexadecimal digits to their ASCII representation using SIMD-style vector operations. + * This method processes multiple digits in parallel by treating a long value as eight 8-bit lanes, + * achieving significantly better performance compared to traditional loop-based conversion. + * + *

The conversion algorithm works as follows: + *

+     * 1. Input expansion: Each 4-bit hex digit is expanded to 8 bits
+     * 2. Vector processing:
+     *    - Add 6 to each digit: triggers carry flag for a-f digits
+     *    - Mask with 0x10 pattern to isolate carry flags
+     *    - Calculate ASCII adjustment: (carry << 1) + (carry >> 1) - (carry >> 4)
+     *    - Add ASCII '0' base (0x30) and original value
+     * 3. Byte order adjustment for final output
+     * 
+ * + *

Performance characteristics: + *

    + *
  • Processes 8 digits in parallel using vector operations + *
  • Avoids branching and loops completely + *
  • Uses only integer arithmetic and bit operations + *
  • Constant time execution regardless of input values + *
+ * + *

ASCII conversion mapping: + *

    + *
  • Digits 0-9 → ASCII '0'-'9' (0x30-0x39) + *
  • Digits a-f → ASCII 'a'-'f' (0x61-0x66) + *
+ * + * @param input A long containing 8 hex digits (each digit must be 0-15) + * @return A long containing 8 ASCII bytes representing the hex digits + * + * @implNote The implementation leverages CPU vector processing capabilities through + * long integer operations. The algorithm is based on the observation that + * ASCII hex digits have a specific pattern that can be computed efficiently + * using carry flag manipulation. + * + * @example + *
+     * Input:  0xABCDEF01
+     * Output: 3130666564636261 ('1','0','f','e','d','c','b','a' in ASCII)
+     * 
+ * + * @see Long#reverseBytes(long) */ - public static long hex8(long i) { - long x = Long.expand(i, 0x0F0F_0F0F_0F0F_0F0FL); + private static long hex8(long i) { + // Expand each 4-bit group into 8 bits, spreading them out in the long value: 0xAABBCCDD -> 0xA0A0B0B0C0C0D0D + i = Long.expand(i, 0x0F0F_0F0F_0F0F_0F0FL); + /* - Use long to simulate vector operations and generate 8 hexadecimal characters at a time. - ------------ - 0 = 0b0000_0000 => m = ((i + 6) & 0x10); (m << 1) + (m >> 1) - (m >> 4) => 0 + 0x30 + (i & 0xF) => '0' - 1 = 0b0000_0001 => m = ((i + 6) & 0x10); (m << 1) + (m >> 1) - (m >> 4) => 0 + 0x30 + (i & 0xF) => '1' - 2 = 0b0000_0010 => m = ((i + 6) & 0x10); (m << 1) + (m >> 1) - (m >> 4) => 0 + 0x30 + (i & 0xF) => '2' - 3 = 0b0000_0011 => m = ((i + 6) & 0x10); (m << 1) + (m >> 1) - (m >> 4) => 0 + 0x30 + (i & 0xF) => '3' - 4 = 0b0000_0100 => m = ((i + 6) & 0x10); (m << 1) + (m >> 1) - (m >> 4) => 0 + 0x30 + (i & 0xF) => '4' - 5 = 0b0000_0101 => m = ((i + 6) & 0x10); (m << 1) + (m >> 1) - (m >> 4) => 0 + 0x30 + (i & 0xF) => '5' - 6 = 0b0000_0110 => m = ((i + 6) & 0x10); (m << 1) + (m >> 1) - (m >> 4) => 0 + 0x30 + (i & 0xF) => '6' - 7 = 0b0000_0111 => m = ((i + 6) & 0x10); (m << 1) + (m >> 1) - (m >> 4) => 0 + 0x30 + (i & 0xF) => '7' - 8 = 0b0000_1000 => m = ((i + 6) & 0x10); (m << 1) + (m >> 1) - (m >> 4) => 0 + 0x30 + (i & 0xF) => '8' - 9 = 0b0000_1001 => m = ((i + 6) & 0x10); (m << 1) + (m >> 1) - (m >> 4) => 0 + 0x30 + (i & 0xF) => '9' - 10 = 0b0000_1010 => m = ((i + 6) & 0x10); (m << 1) + (m >> 1) - (m >> 4) => 39 + 0x30 + (i & 0xF) => 'a' - 11 = 0b0000_1011 => m = ((i + 6) & 0x10); (m << 1) + (m >> 1) - (m >> 4) => 39 + 0x30 + (i & 0xF) => 'b' - 12 = 0b0000_1100 => m = ((i + 6) & 0x10); (m << 1) + (m >> 1) - (m >> 4) => 39 + 0x30 + (i & 0xF) => 'c' - 13 = 0b0000_1101 => m = ((i + 6) & 0x10); (m << 1) + (m >> 1) - (m >> 4) => 39 + 0x30 + (i & 0xF) => 'd' - 14 = 0b0000_1110 => m = ((i + 6) & 0x10); (m << 1) + (m >> 1) - (m >> 4) => 39 + 0x30 + (i & 0xF) => 'e' - 15 = 0b0000_1111 => m = ((i + 6) & 0x10); (m << 1) + (m >> 1) - (m >> 4) => 39 + 0x30 + (i & 0xF) => 'f' + * This method efficiently converts 8 hexadecimal digits simultaneously using vector operations + * The algorithm works as follows: + * + * For input values 0-15: + * - For digits 0-9: converts to ASCII '0'-'9' (0x30-0x39) + * - For digits 10-15: converts to ASCII 'a'-'f' (0x61-0x66) + * + * The conversion process: + * 1. Add 6 to each 4-bit group: i + 0x0606_0606_0606_0606L + * 2. Mask to get the adjustment flags: & 0x1010_1010_1010_1010L + * 3. Calculate the offset: (m << 1) + (m >> 1) - (m >> 4) + * - For 0-9: offset = 0 + * - For a-f: offset = 39 (to bridge the gap between '9' and 'a' in ASCII) + * 4. Add ASCII '0' base (0x30) and the original value + * 5. Reverse byte order for correct positioning */ - long m = (x + 0x0606_0606_0606_0606L) & 0x1010_1010_1010_1010L; - return ((m << 1) + (m >> 1) - (m >> 4)) - + 0x3030_3030_3030_3030L - + x; + long m = (i + 0x0606_0606_0606_0606L) & 0x1010_1010_1010_1010L; + + // Calculate final ASCII values and reverse bytes for proper ordering + return Long.reverseBytes( + ((m << 1) + (m >> 1) - (m >> 4)) + + 0x3030_3030_3030_3030L // Add ASCII '0' base to all digits + + i // Add original values + ); } } From 4de8e57b4d30b1d6bf2d74d057d6f0c354b783a1 Mon Sep 17 00:00:00 2001 From: Shaojin Wen Date: Fri, 23 May 2025 08:55:40 +0800 Subject: [PATCH 08/14] public hex8 --- src/java.base/share/classes/jdk/internal/util/HexDigits.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/java.base/share/classes/jdk/internal/util/HexDigits.java b/src/java.base/share/classes/jdk/internal/util/HexDigits.java index 61d9ca0986714..829ab73d4a225 100644 --- a/src/java.base/share/classes/jdk/internal/util/HexDigits.java +++ b/src/java.base/share/classes/jdk/internal/util/HexDigits.java @@ -227,7 +227,7 @@ public static int stringSize(long value) { * * @see Long#reverseBytes(long) */ - private static long hex8(long i) { + public static long hex8(long i) { // Expand each 4-bit group into 8 bits, spreading them out in the long value: 0xAABBCCDD -> 0xA0A0B0B0C0C0D0D i = Long.expand(i, 0x0F0F_0F0F_0F0F_0F0FL); From 99feb9ac2b0c570e38dfa69087820ad9397a5b88 Mon Sep 17 00:00:00 2001 From: Shaojin Wen Date: Fri, 23 May 2025 09:43:00 +0800 Subject: [PATCH 09/14] reverseBytes --- src/java.base/share/classes/java/util/UUID.java | 6 +++--- .../share/classes/jdk/internal/util/HexDigits.java | 9 +++------ 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/src/java.base/share/classes/java/util/UUID.java b/src/java.base/share/classes/java/util/UUID.java index bcb133744787c..cc57e803f1206 100644 --- a/src/java.base/share/classes/java/util/UUID.java +++ b/src/java.base/share/classes/java/util/UUID.java @@ -471,14 +471,14 @@ public String toString() { // Although the UUID byte ordering is defined to be big-endian, ByteArrayLittleEndian is used here to optimize // for the most common architectures. hex8 reverses the order internally. ByteArrayLittleEndian.setLong(buf, 0, HexDigits.hex8(mostSigBits >>> 32)); - long x0 = HexDigits.hex8(mostSigBits); + long x0 = Long.reverseBytes(HexDigits.hex8(mostSigBits)); ByteArrayLittleEndian.setInt(buf, 9, (int) x0); ByteArrayLittleEndian.setInt(buf, 14, (int) (x0 >>> 32)); - long x1 = HexDigits.hex8(leastSigBits >>> 32); + long x1 = Long.reverseBytes(HexDigits.hex8(leastSigBits >>> 32)); ByteArrayLittleEndian.setInt(buf, 19, (int) (x1)); ByteArrayLittleEndian.setInt(buf, 24, (int) (x1 >>> 32)); - ByteArrayLittleEndian.setLong(buf, 28, HexDigits.hex8(leastSigBits)); + ByteArrayLittleEndian.setLong(buf, 28, Long.reverseBytes(HexDigits.hex8(leastSigBits))); try { return jla.uncheckedNewStringNoRepl(buf, StandardCharsets.ISO_8859_1); diff --git a/src/java.base/share/classes/jdk/internal/util/HexDigits.java b/src/java.base/share/classes/jdk/internal/util/HexDigits.java index 829ab73d4a225..9c3d0defa9a4c 100644 --- a/src/java.base/share/classes/jdk/internal/util/HexDigits.java +++ b/src/java.base/share/classes/jdk/internal/util/HexDigits.java @@ -225,7 +225,6 @@ public static int stringSize(long value) { * Output: 3130666564636261 ('1','0','f','e','d','c','b','a' in ASCII) * * - * @see Long#reverseBytes(long) */ public static long hex8(long i) { // Expand each 4-bit group into 8 bits, spreading them out in the long value: 0xAABBCCDD -> 0xA0A0B0B0C0C0D0D @@ -251,10 +250,8 @@ public static long hex8(long i) { long m = (i + 0x0606_0606_0606_0606L) & 0x1010_1010_1010_1010L; // Calculate final ASCII values and reverse bytes for proper ordering - return Long.reverseBytes( - ((m << 1) + (m >> 1) - (m >> 4)) - + 0x3030_3030_3030_3030L // Add ASCII '0' base to all digits - + i // Add original values - ); + return ((m << 1) + (m >> 1) - (m >> 4)) + + 0x3030_3030_3030_3030L // Add ASCII '0' base to all digits + + i; // Add original values } } From 497b197ce92195e6027f9d5b7968f0251b237bdf Mon Sep 17 00:00:00 2001 From: Shaojin Wen Date: Fri, 23 May 2025 13:06:42 +0800 Subject: [PATCH 10/14] reverseBytes --- src/java.base/share/classes/java/util/UUID.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/java.base/share/classes/java/util/UUID.java b/src/java.base/share/classes/java/util/UUID.java index cc57e803f1206..17e6cfea562cb 100644 --- a/src/java.base/share/classes/java/util/UUID.java +++ b/src/java.base/share/classes/java/util/UUID.java @@ -470,7 +470,7 @@ public String toString() { // Although the UUID byte ordering is defined to be big-endian, ByteArrayLittleEndian is used here to optimize // for the most common architectures. hex8 reverses the order internally. - ByteArrayLittleEndian.setLong(buf, 0, HexDigits.hex8(mostSigBits >>> 32)); + ByteArrayLittleEndian.setLong(buf, 0, Long.reverseBytes(HexDigits.hex8(mostSigBits >>> 32))); long x0 = Long.reverseBytes(HexDigits.hex8(mostSigBits)); ByteArrayLittleEndian.setInt(buf, 9, (int) x0); ByteArrayLittleEndian.setInt(buf, 14, (int) (x0 >>> 32)); From 515ffa092b80ffd51f974d4e535fa80ee2061899 Mon Sep 17 00:00:00 2001 From: Shaojin Wen Date: Sat, 31 May 2025 05:54:49 +0800 Subject: [PATCH 11/14] fix benchmark --- .../org/openjdk/bench/java/lang/Integers.java | 16 +++++++++++++--- .../micro/org/openjdk/bench/java/lang/Longs.java | 10 ++++++++-- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/test/micro/org/openjdk/bench/java/lang/Integers.java b/test/micro/org/openjdk/bench/java/lang/Integers.java index 404122cfbaa37..3e158a33fabfc 100644 --- a/test/micro/org/openjdk/bench/java/lang/Integers.java +++ b/test/micro/org/openjdk/bench/java/lang/Integers.java @@ -58,6 +58,9 @@ public class Integers { private int[] intsSmall; private int[] intsBig; private int[] res; + private int[] hexsTiny; + private int[] hexsSmall; + private int[] hexsBig; @Setup public void setup() { @@ -68,11 +71,18 @@ public void setup() { intsSmall = new int[size]; intsBig = new int[size]; res = new int[size]; + hexsTiny = new int[size]; + hexsSmall = new int[size]; + hexsBig = new int[size]; for (int i = 0; i < size; i++) { strings[i] = "" + (r.nextInt(10000) - (5000)); intsTiny[i] = r.nextInt(99); intsSmall[i] = 100 * i + i + 103; intsBig[i] = ((100 * i + i) << 24) + 4543 + i * 4; + + hexsTiny[i] = r.nextInt(0xFF); + hexsSmall[i] = 0x100 * i + i + 0x103; + hexsBig[i] = ((0x100 * i + i) << 24) + 0x4543 + i * 4; } } @@ -116,21 +126,21 @@ public void toStringBig(Blackhole bh) { @Benchmark public void toHexStringTiny(Blackhole bh) { - for (int i : intsTiny) { + for (int i : hexsTiny) { bh.consume(Integer.toHexString(i)); } } @Benchmark public void toHexStringSmall(Blackhole bh) { - for (int i : intsSmall) { + for (int i : hexsSmall) { bh.consume(Integer.toHexString(i)); } } @Benchmark public void toHexStringBig(Blackhole bh) { - for (int i : intsBig) { + for (int i : hexsBig) { bh.consume(Integer.toHexString(i)); } } diff --git a/test/micro/org/openjdk/bench/java/lang/Longs.java b/test/micro/org/openjdk/bench/java/lang/Longs.java index dac6c1b4a401c..77ed5ce681598 100644 --- a/test/micro/org/openjdk/bench/java/lang/Longs.java +++ b/test/micro/org/openjdk/bench/java/lang/Longs.java @@ -54,6 +54,8 @@ public class Longs { private String[] strings; private long[] longArraySmall; private long[] longArrayBig; + private long[] hexsArraySmall; + private long[] hexsArrayBig; @Setup public void setup() { @@ -63,10 +65,14 @@ public void setup() { res = new long[size]; longArraySmall = new long[size]; longArrayBig = new long[size]; + hexsArraySmall = new long[size]; + hexsArrayBig = new long[size]; for (int i = 0; i < size; i++) { strings[i] = "" + (random.nextLong(10000) - 5000); longArraySmall[i] = 100L * i + i + 103L; longArrayBig[i] = ((100L * i + i) << 32) + 4543 + i * 4L; + hexsArraySmall[i] = 0x100L * i + i + 0x103L; + hexsArrayBig[i] = ((0x100L * i + i) << 32) + 0x4543 + i * 4L; } } @@ -95,14 +101,14 @@ public void toStringBig(Blackhole bh) { @Benchmark public void toHexStringSmall(Blackhole bh) { - for (long value : longArraySmall) { + for (long value : hexsArraySmall) { bh.consume(Long.toHexString(value)); } } @Benchmark public void toHexStringBig(Blackhole bh) { - for (long value : longArrayBig) { + for (long value : hexsArrayBig) { bh.consume(Long.toHexString(value)); } } From 8a30d2ec75ad7be481c063aa240096998883b0b0 Mon Sep 17 00:00:00 2001 From: Shaojin Wen Date: Thu, 12 Jun 2025 19:45:40 +0800 Subject: [PATCH 12/14] use right shift --- src/java.base/share/classes/java/lang/Long.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/java.base/share/classes/java/lang/Long.java b/src/java.base/share/classes/java/lang/Long.java index 63b26101759b6..87177cdef41c7 100644 --- a/src/java.base/share/classes/java/lang/Long.java +++ b/src/java.base/share/classes/java/lang/Long.java @@ -315,7 +315,7 @@ private static BigInteger toUnsignedBigInteger(long i) { */ public static String toHexString(long i) { int mag = Long.SIZE - Long.numberOfLeadingZeros(i); - int len = Math.max(((mag + 3) / 4), 1); + int len = Math.max(((mag + 3) >> 2), 1); long x = HexDigits.hex8(i); if (COMPACT_STRINGS) { byte[] chars = new byte[len]; From 9335e85ce159a5d7884fdd98166d0d689a5e2250 Mon Sep 17 00:00:00 2001 From: Shaojin Wen Date: Thu, 12 Jun 2025 21:03:12 +0800 Subject: [PATCH 13/14] use right shift --- src/java.base/share/classes/java/lang/Integer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/java.base/share/classes/java/lang/Integer.java b/src/java.base/share/classes/java/lang/Integer.java index 22dfb6c3b3d66..67c9dd29a8473 100644 --- a/src/java.base/share/classes/java/lang/Integer.java +++ b/src/java.base/share/classes/java/lang/Integer.java @@ -289,7 +289,7 @@ public static String toUnsignedString(int i, int radix) { */ public static String toHexString(int i) { int mag = Integer.SIZE - Integer.numberOfLeadingZeros(i); - int len = Math.max(((mag + 3) / 4), 1); + int len = Math.max(((mag + 3) >> 2), 1); long x = HexDigits.hex8(i); if (COMPACT_STRINGS) { byte[] chars = new byte[len]; From 9b9bfb1db8c273ea615dac44e63784f1d598dd4e Mon Sep 17 00:00:00 2001 From: Shaojin Wen Date: Sun, 15 Jun 2025 13:49:42 +0800 Subject: [PATCH 14/14] Update src/java.base/share/classes/java/util/UUID.java Co-authored-by: ExE Boss <3889017+ExE-Boss@users.noreply.github.com> --- src/java.base/share/classes/java/util/UUID.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/java.base/share/classes/java/util/UUID.java b/src/java.base/share/classes/java/util/UUID.java index 17e6cfea562cb..8fb07cba04341 100644 --- a/src/java.base/share/classes/java/util/UUID.java +++ b/src/java.base/share/classes/java/util/UUID.java @@ -469,7 +469,7 @@ public String toString() { buf[23] = '-'; // Although the UUID byte ordering is defined to be big-endian, ByteArrayLittleEndian is used here to optimize - // for the most common architectures. hex8 reverses the order internally. + // for the most common architectures. ByteArrayLittleEndian.setLong(buf, 0, Long.reverseBytes(HexDigits.hex8(mostSigBits >>> 32))); long x0 = Long.reverseBytes(HexDigits.hex8(mostSigBits)); ByteArrayLittleEndian.setInt(buf, 9, (int) x0);