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:
+8
-41
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user