perf: direct Math.unsignedMultiplyHigh call — eliminate MethodHandle boxing

The JVM target is Java 21, so Math.unsignedMultiplyHigh (Java 18+) can
be called directly without MethodHandle reflection. The previous approach
used MethodHandle.invokeExact which Kotlin compiles with Object return
type, causing Long boxing on every call (3 box/unbox per invocation ×
16 calls per field multiply = 48 boxed objects per mul).

Direct call compiles to a single UMULH instruction with zero overhead.
This is the most performance-critical function: called ~12,000× per
signature verification.

https://claude.ai/code/session_01BhU63WUe9AhikZxRdw3Lpg
This commit is contained in:
Claude
2026-04-06 19:01:38 +00:00
parent 7e8a060f17
commit 09427fe6cf
@@ -31,48 +31,15 @@ internal actual fun multiplyHigh(
): Long = Math.multiplyHigh(a, b)
/**
* JVM: uses Math.unsignedMultiplyHigh (Java 18+) if available, else fallback.
* The HAS_UNSIGNED_MULTIPLY_HIGH check is evaluated once at class init and the
* JIT will devirtualize the hot branch after a few invocations.
* JVM implementation using Math.unsignedMultiplyHigh (Java 18+).
* Direct call — no MethodHandle, no boxing, no branch.
* The JIT compiles this to a single UMULH instruction on x86-64/ARM64.
*
* This is the single most performance-critical function in the entire
* secp256k1 implementation: called 16× per field multiply, ~12,000×
* per signature verification.
*/
internal actual fun unsignedMultiplyHigh(
a: Long,
b: Long,
): Long =
if (HAS_UNSIGNED_MULTIPLY_HIGH) {
unsignedMultiplyHighNative(a, b)
} else {
unsignedMultiplyHighFallback(a, b)
}
/**
* Tries to resolve Math.unsignedMultiplyHigh (Java 18+) as a MethodHandle.
* If available, invoking it compiles to a single UMULH instruction, saving
* 4 correction instructions per product vs the signed multiplyHigh + fixup path.
* This saves ~64 instructions per field multiplication (16 products × 4 insns).
*
* MethodHandle.invokeExact is JIT-inlined to the same cost as a direct call.
*/
private val UNSIGNED_MUL_HIGH: java.lang.invoke.MethodHandle? =
try {
java.lang.invoke.MethodHandles.lookup().findStatic(
Math::class.java,
"unsignedMultiplyHigh",
java.lang.invoke.MethodType.methodType(
java.lang.Long.TYPE,
java.lang.Long.TYPE,
java.lang.Long.TYPE,
),
)
} catch (_: Throwable) {
null
}
/** True if the native unsigned multiply high is available (Java 18+). */
internal val HAS_UNSIGNED_MULTIPLY_HIGH: Boolean = UNSIGNED_MUL_HIGH != null
/** Call Math.unsignedMultiplyHigh via MethodHandle (only when HAS_UNSIGNED_MULTIPLY_HIGH is true). */
internal fun unsignedMultiplyHighNative(
a: Long,
b: Long,
): Long = UNSIGNED_MUL_HIGH!!.invokeExact(a, b) as Long
): Long = Math.unsignedMultiplyHigh(a, b)