mruby-bigint: optimize multiplication for power-of-2 numbers

Add fast path for multiplying by powers of 2 (2^n). Uses left shift
instead of Karatsuba multiplication: x * 2^n = x << n.

This optimizes "mostly-zero" patterns common in fuzzing tests, where
numbers like 2^2097150 (single bit set) would otherwise trigger slow
Karatsuba multiplication.

Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
Yukihiro "Matz" Matsumoto
2026-01-09 12:29:50 +09:00
parent b7593cde15
commit ef64ca32a1
+38
View File
@@ -1146,6 +1146,32 @@ mpz_all_ones_p(mpz_t *x)
return (x->sz - 1) * DIG_SIZE + top_bits;
}
/*
* Check if x is a power of 2 (2^n).
* Returns n if x = 2^n, or 0 otherwise.
* This is the "mostly-zero" pattern common in fuzzing tests.
*/
static size_t
mpz_power_of_2_exp(mpz_t *x)
{
if (x->sn <= 0 || x->sz == 0) return 0;
/* All limbs except top must be zero */
for (size_t i = 0; i + 1 < x->sz; i++) {
if (x->p[i] != 0) return 0;
}
/* Top limb must be a power of 2: (v & (v - 1)) == 0 */
mp_limb top = x->p[x->sz - 1];
if (top == 0 || (top & (top - 1)) != 0) return 0;
/* Count trailing zeros in top limb to get bit position */
size_t bit_pos = 0;
while ((top & 1) == 0) { bit_pos++; top >>= 1; }
return (x->sz - 1) * DIG_SIZE + bit_pos;
}
/*
* Multiply two "all ones" numbers using algebraic identity:
* (2^n - 1) * (2^m - 1) = 2^(n+m) - 2^n - 2^m + 1
@@ -1239,6 +1265,18 @@ mpz_mul(mpz_ctx_t *ctx, mpz_t *ww, mpz_t *u, mpz_t *v)
return;
}
/* Fast path for power of 2: x * 2^n = x << n */
size_t u_pow2 = mpz_power_of_2_exp(u);
if (u_pow2) {
mpz_mul_2exp(ctx, ww, v, u_pow2);
return;
}
size_t v_pow2 = mpz_power_of_2_exp(v);
if (v_pow2) {
mpz_mul_2exp(ctx, ww, u, v_pow2);
return;
}
if (!should_use_karatsuba(u->sz, v->sz)) {
mpz_mul_basic(ctx, ww, u, v);
return;