perf: ARM64-specific optimizations for mobile phones
Optimize the ARM64 code path (the primary target for Nostr clients): 1. fe_mul_asm: Use LDP/STP (load/store pair) to load all 8 limbs in 4 instructions instead of 8 individual LDR. Row 0 in hand-tuned ASM with MUL+UMULH+ADDS/ADC. Rows 1-3 in __int128 C (ARM64 gcc generates optimal MUL+UMULH+ADDS/ADCS and MADD from this). 2. fe_normalize: Branchless on ARM64 using mask-based conditional subtract (compiles to CSEL/AND on ARM64, avoiding branch misprediction on mobile Cortex-A76+ SoCs). x86_64 keeps the branching version since its branch predictor handles the >99.99% non-taken case perfectly. 3. Document ARM64-specific instruction usage: - MUL + UMULH: 64×64→128 product (1 cycle throughput on A76+) - LDP/STP: load/store pair (2 regs per instruction) - ADDS/ADCS: carry chain for accumulation - MADD: fused multiply-add (generated by gcc from __int128) These changes don't affect x86_64 correctness or performance (verified: all keys pass, benchmark matches previous numbers). The ARM64 improvements will be measurable when built with the Android NDK for actual phone testing. https://claude.ai/code/session_011KVZhDcV2G7idNWEBz12GY
This commit is contained in:
@@ -67,8 +67,21 @@ static inline int fe_is_odd(const secp256k1_fe *a) {
|
||||
return (int)(t.d[0] & 1);
|
||||
}
|
||||
|
||||
/* Normalize: if a >= p, subtract p. Inline for hot path. */
|
||||
/* Normalize: if a >= p, subtract p.
|
||||
* On ARM64: branchless (CSEL avoids misprediction on mobile SoCs).
|
||||
* On x86_64: branching (>99.99% correct prediction, branch is cheaper). */
|
||||
static inline void fe_normalize(secp256k1_fe *a) {
|
||||
#if SECP_ARM64
|
||||
/* Branchless for ARM64: compute mask, conditionally subtract */
|
||||
uint64_t ge = (a->d[3] == UINT64_MAX) & (a->d[2] == UINT64_MAX) &
|
||||
(a->d[1] == UINT64_MAX) & (a->d[0] >= FE_P0);
|
||||
uint64_t mask = -(uint64_t)ge;
|
||||
a->d[0] -= FE_P0 & mask;
|
||||
a->d[1] &= ~mask;
|
||||
a->d[2] &= ~mask;
|
||||
a->d[3] &= ~mask;
|
||||
#else
|
||||
/* Branching for x86_64: branch predictor handles the >99.99% case */
|
||||
if (a->d[3] == UINT64_MAX && a->d[2] == UINT64_MAX &&
|
||||
a->d[1] == UINT64_MAX && a->d[0] >= FE_P0) {
|
||||
a->d[0] -= FE_P0;
|
||||
@@ -76,6 +89,7 @@ static inline void fe_normalize(secp256k1_fe *a) {
|
||||
a->d[2] = 0;
|
||||
a->d[3] = 0;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void fe_normalize_full(secp256k1_fe *a) {
|
||||
|
||||
Reference in New Issue
Block a user