diff --git a/pom.xml b/pom.xml
index 714398b..b2fb805 100644
--- a/pom.xml
+++ b/pom.xml
@@ -38,6 +38,9 @@
5.1.1
all,-missing
+ 1.37
+ openhft
+ https://sonarcloud.io
@@ -74,6 +77,20 @@
test
+
+ org.openjdk.jmh
+ jmh-core
+ ${jmh.version}
+ test
+
+
+
+ org.openjdk.jmh
+ jmh-generator-annprocess
+ ${jmh.version}
+ test
+
+
@@ -273,7 +290,6 @@
-
diff --git a/src/main/java/net/openhft/hashing/Maths.java b/src/main/java/net/openhft/hashing/Maths.java
index 40ed4a0..ee6c251 100644
--- a/src/main/java/net/openhft/hashing/Maths.java
+++ b/src/main/java/net/openhft/hashing/Maths.java
@@ -5,29 +5,26 @@
import org.jetbrains.annotations.NotNull;
-import java.lang.invoke.MethodHandle;
-import java.lang.invoke.MethodHandles;
-import java.lang.reflect.Method;
-
+@SuppressWarnings("Since15")
class Maths {
@NotNull
private static final Maths INSTANCE;
static {
- Maths maths = null;
+ boolean hasMultiplyHigh = true;
try {
- Method multiplyHigh = Math.class.getDeclaredMethod("multiplyHigh", int.class, int.class);
- MethodHandle multiplyHighMH = MethodHandles.lookup().unreflect(multiplyHigh);
- maths = new MathsJDK9(multiplyHighMH);
+ // stubs in src/main/java-stub are used to allow this to compile in Java 8+
+ Math.multiplyHigh(0, 0);
} catch (final Throwable ignore) {
- maths = new Maths();
+ hasMultiplyHigh = false;
}
- INSTANCE = maths;
+ INSTANCE = hasMultiplyHigh ? new MathsJDK9() : new Maths();
}
public static long unsignedLongMulXorFold(final long lhs, final long rhs) {
return INSTANCE.unsignedLongMulXorFoldImp(lhs, rhs);
}
+
public static long unsignedLongMulHigh(final long lhs, final long rhs) {
return INSTANCE.unsignedLongMulHighImp(lhs, rhs);
}
@@ -70,31 +67,19 @@ long unsignedLongMulHighImp(final long lhs, final long rhs) {
}
}
+@SuppressWarnings("Since15")
class MathsJDK9 extends Maths {
- private final MethodHandle multiplyHighMH;
-
- public MathsJDK9(MethodHandle multiplyHighMH) {
- this.multiplyHighMH = multiplyHighMH;
- }
-
// Math.multiplyHigh() is intrinsified from JDK 10. But JDK 9 is out of life, we always prefer
// this version to the scalar one.
@Override
long unsignedLongMulXorFoldImp(final long lhs, final long rhs) {
- final long upper = invokeExact(lhs, rhs) + ((lhs >> 63) & rhs) + ((rhs >> 63) & lhs);
+ final long upper = Math.multiplyHigh(lhs, rhs) + ((lhs >> 63) & rhs) + ((rhs >> 63) & lhs);
final long lower = lhs * rhs;
return lower ^ upper;
}
+
@Override
long unsignedLongMulHighImp(final long lhs, final long rhs) {
- return invokeExact(lhs, rhs) + ((lhs >> 63) & rhs) + ((rhs >> 63) & lhs);
- }
-
- private long invokeExact(long lhs, long rhs) {
- try {
- return (long) multiplyHighMH.invokeExact(lhs, rhs);
- } catch (Throwable e) {
- throw new AssertionError(e);
- }
+ return Math.multiplyHigh(lhs, rhs) + ((lhs >> 63) & rhs) + ((rhs >> 63) & lhs);
}
}
diff --git a/src/test/java/net/openhft/hashing/XXH3HashBytesBenchmark.java b/src/test/java/net/openhft/hashing/XXH3HashBytesBenchmark.java
new file mode 100644
index 0000000..582e744
--- /dev/null
+++ b/src/test/java/net/openhft/hashing/XXH3HashBytesBenchmark.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2013-2025 chronicle.software; SPDX-License-Identifier: Apache-2.0
+ */
+package net.openhft.hashing;
+
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.BenchmarkMode;
+import org.openjdk.jmh.annotations.Fork;
+import org.openjdk.jmh.annotations.Mode;
+import org.openjdk.jmh.annotations.OutputTimeUnit;
+import org.openjdk.jmh.annotations.Scope;
+import org.openjdk.jmh.annotations.State;
+
+import java.io.IOException;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Reproduces issue #101 for XXH3 hashing of a 128 byte array.
+ */
+@State(Scope.Thread)
+@BenchmarkMode(Mode.Throughput)
+@OutputTimeUnit(TimeUnit.MILLISECONDS)
+@Fork(
+ value = 5,
+ warmups = 3,
+ jvmArgs = {
+ "-XX:-TieredCompilation",
+ "-XX:+UseParallelGC",
+ "-Xms16g",
+ "-Xmx16g",
+ })
+public class XXH3HashBytesBenchmark {
+ private static final int INPUT_LENGTH_BYTES = 128;
+ private static final LongHashFunction XXH3_64_HASH_FUNCTION = LongHashFunction.xx3();
+ private static final byte[] ZERO_FILLED_128_BYTE_INPUT = new byte[INPUT_LENGTH_BYTES];
+
+ @Benchmark
+ public long hashZeroFilledByteArray128Bytes() {
+ return XXH3_64_HASH_FUNCTION.hashBytes(ZERO_FILLED_128_BYTE_INPUT);
+ }
+
+ public static void main(String[] args) throws IOException {
+ org.openjdk.jmh.Main.main(args);
+ }
+}