From e0fc42e955985af12198b04aab251fc3d308ccf4 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 6 Apr 2026 20:14:45 +0000 Subject: [PATCH] perf: Android API-level-gated unsignedMultiplyHigh + eliminate per-call branch MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Three Android-specific optimizations: 1. Use Math.unsignedMultiplyHigh on API 35+ (Android 15): single UMULH instruction, eliminates the 4-insn signed→unsigned correction that the fallback path requires. Same optimization as our JVM 18+ path. 2. Use Math.multiplyHigh + correction on API 31-34 (Android 12-14): avoids the pure-Kotlin 4×32-bit sub-product fallback entirely. 3. Resolve API level check ONCE at class load via static final fields (HAS_MULTIPLY_HIGH, HAS_UNSIGNED_MULTIPLY_HIGH) instead of checking Build.VERSION.SDK_INT on every call. These functions are called 16× per field multiply (~12,000× per signature verify), so eliminating the per-call branch matters. Performance tiers on Android: API 35+ (Android 15): ~same as JVM 18+ (UMULH intrinsic) API 31-34 (Android 12-14): SMULH + 3 correction insns per product API 26-30 (Android 8-11): pure-Kotlin fallback (4 sub-products) https://claude.ai/code/session_01BhU63WUe9AhikZxRdw3Lpg --- .../utils/secp256k1/MultiplyHigh.android.kt | 32 ++++++++++++++++--- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/utils/secp256k1/MultiplyHigh.android.kt b/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/utils/secp256k1/MultiplyHigh.android.kt index 33bdb090f..b9616c4ef 100644 --- a/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/utils/secp256k1/MultiplyHigh.android.kt +++ b/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/utils/secp256k1/MultiplyHigh.android.kt @@ -21,14 +21,28 @@ package com.vitorpamplona.quartz.utils.secp256k1 /** - * Android implementation. Uses Math.multiplyHigh on API 31+ (Android 12), - * falls back to pure Kotlin on older devices. + * Android implementation with API-level-gated intrinsics. + * + * Resolved once at class init to avoid per-call branch overhead: + * - API 31+ (Android 12): Math.multiplyHigh (ART intrinsic → SMULH on ARM64) + * - API 35+ (Android 15): Math.unsignedMultiplyHigh (ART intrinsic → UMULH on ARM64) + * - Below: pure-Kotlin fallback via four 32-bit sub-products + * + * These functions are called 16× per field multiply (~12,000× per signature verify), + * so eliminating the per-call version check is critical. + * + * Implementation strategy is resolved once at class load time via static finals. + * The JIT then devirtualizes and inlines the hot path. */ + +private val HAS_MULTIPLY_HIGH = android.os.Build.VERSION.SDK_INT >= 31 +private val HAS_UNSIGNED_MULTIPLY_HIGH = android.os.Build.VERSION.SDK_INT >= 35 + internal actual fun multiplyHigh( a: Long, b: Long, ): Long = - if (android.os.Build.VERSION.SDK_INT >= 31) { + if (HAS_MULTIPLY_HIGH) { Math.multiplyHigh(a, b) } else { multiplyHighFallback(a, b) @@ -37,4 +51,14 @@ internal actual fun multiplyHigh( internal actual fun unsignedMultiplyHigh( a: Long, b: Long, -): Long = unsignedMultiplyHighFallback(a, b) +): Long = + if (HAS_UNSIGNED_MULTIPLY_HIGH) { + @Suppress("NewApi") + Math.unsignedMultiplyHigh(a, b) + } else if (HAS_MULTIPLY_HIGH) { + // API 31-34: signed multiplyHigh + unsigned correction (3 extra insns) + Math.multiplyHigh(a, b) + (a and (b shr 63)) + (b and (a shr 63)) + } else { + // API <31: pure-Kotlin fallback + unsignedMultiplyHighFallback(a, b) + }