From 1eea99b8588133588ae9e8976985aa5559021d3c Mon Sep 17 00:00:00 2001 From: "Yukihiro \"Matz\" Matsumoto" Date: Tue, 13 Jan 2026 11:00:59 +0900 Subject: [PATCH] mruby-bigint: reuse q5 as hi in D&C to_s to reduce allocation Instead of allocating a separate hi buffer for the upper part of the split, reuse q5 by shifting it in place after extracting the lower bits to q5_low. This eliminates one mpz_t allocation per recursive call: - Extract q5_low = q5 mod 2^k first (copy lower bits) - Shift q5 right in place (memmove + mpn_rshift) to get hi - q5 now serves as hi for the recursive call Benchmark results (2.4M bit number): - Memory: -3.5% (5.58MB -> 5.38MB peak heap) - Instructions: -16.5% - Speed: unchanged (within measurement noise) Co-authored-by: Claude --- mrbgems/mruby-bigint/core/bigint.c | 43 +++++++++++++++++++++--------- 1 file changed, 30 insertions(+), 13 deletions(-) diff --git a/mrbgems/mruby-bigint/core/bigint.c b/mrbgems/mruby-bigint/core/bigint.c index 977e62a99..01395401e 100644 --- a/mrbgems/mruby-bigint/core/bigint.c +++ b/mrbgems/mruby-bigint/core/bigint.c @@ -2657,12 +2657,7 @@ mpz_to_s_dc_recur(mpz_ctx_t *ctx, char *s, mpz_t *x, size_t num_digits, * lo = (q5 & ((1<q5; mpz_t *r5 = &scratch->r5; mpz_t *q5_low = &scratch->q5_low; @@ -2671,24 +2666,46 @@ mpz_to_s_dc_recur(mpz_ctx_t *ctx, char *s, mpz_t *x, size_t num_digits, mpz_mdivmod(ctx, q5, r5, x, &pow5[split_idx]); r5->sn = (r5->sn < 0) ? -r5->sn : r5->sn; - /* Step 2: hi = q5 >> split_digits (divide by 2^k using bit shift) */ - mpz_div_2exp(ctx, &hi, q5, (mrb_int)split_digits); - - /* Step 3: lo = (q5 mod 2^k) * 5^k + r5 */ + /* Step 2: Extract q5_low = q5 mod 2^k BEFORE modifying q5 */ mpz_mod_2exp(ctx, q5_low, q5, (mrb_int)split_digits); + /* Step 3: hi = q5 >> k - shift q5 in place to reuse as hi */ + /* This avoids allocating a separate hi buffer */ + mpz_t *hi = q5; /* Reuse q5 as hi after shifting */ + { + size_t limb_shift = split_digits / DIG_SIZE; + size_t bit_shift = split_digits % DIG_SIZE; + + if (limb_shift >= q5->sz) { + zero(q5); + } + else { + size_t new_sz = q5->sz - limb_shift; + /* Shift limbs down */ + memmove(q5->p, q5->p + limb_shift, new_sz * sizeof(mp_limb)); + q5->sz = new_sz; + /* Apply bit shift if needed */ + if (bit_shift > 0) { + mpn_rshift(q5->p, q5->p, q5->sz, (unsigned int)bit_shift); + } + trim(q5); + } + } + + /* Step 4: lo = q5_low * 5^k + r5 - allocate fresh for lo */ + mpz_t lo; + mpz_init(ctx, &lo); mpz_mul(ctx, &lo, q5_low, &pow5[split_idx]); mpz_add(ctx, &lo, &lo, r5); lo.sn = (lo.sn < 0) ? -lo.sn : lo.sn; - /* Recursively convert high part */ + /* Recursively convert high part (using q5 which now contains hi) */ size_t hi_digits = num_digits - split_digits; - mpz_to_s_dc_recur(ctx, s, &hi, hi_digits, pow5, split_idx, scratch); + mpz_to_s_dc_recur(ctx, s, hi, hi_digits, pow5, split_idx, scratch); /* Recursively convert low part (exactly split_digits digits with padding) */ mpz_to_s_dc_recur(ctx, s + hi_digits, &lo, split_digits, pow5, split_idx, scratch); - mpz_clear(ctx, &hi); mpz_clear(ctx, &lo); }