perf: Android API-level-gated unsignedMultiplyHigh + eliminate per-call branch
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
This commit is contained in:
+28
-4
@@ -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)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user