Skip to content

Use word instead of bool for add/sub carry chain - #73

Merged
cmpute merged 1 commit into
masterfrom
word-carry
Jun 11, 2026
Merged

Use word instead of bool for add/sub carry chain#73
cmpute merged 1 commit into
masterfrom
word-carry

Conversation

@cmpute

@cmpute cmpute commented Jun 11, 2026

Copy link
Copy Markdown
Owner

Summary

Change the carry/borrow type in all architecture-specific add_with_carry and sub_with_borrow functions from bool to Word, and use comparison-based overflow detection on the generic
code path. This eliminates bool↔Word conversions in the inner loops and reduces the per-word instruction count from ~9 to ~7 on ARM64.

Changes

7 files, +49/−40 lines

Arch functions (integer/src/arch/{generic,x86,x86_64}/add.rs)

  • add_with_carry(a: Word, b: Word, carry: bool) -> (Word, bool) → (Word, Word, Word) -> (Word, Word)
  • sub_with_borrow(a: Word, b: Word, borrow: bool) -> (Word, bool) → (Word, Word, Word) -> (Word, Word)
  • Generic path: switched from overflowing_add/overflowing_sub to wrapping_add/wrapping_sub with comparison-based overflow detection (sum < a). This generates better ARM64 code
    (ADDS+CSET+ADDS+CSINC instead of ADDS+CSET+ORR).
  • x86/x86_64 paths: adapted intrinsic calls with (carry & 1) as u8 on input and Word::from(carry) on output.

Hot loops (integer/src/add.rs)

  • add_same_len_in_place, sub_same_len_in_place, sub_same_len_in_place_swap: carry/borrow loop variable changed from bool to Word, return carry != 0.
  • Added #[inline] on all five hot functions to ensure the comparison-based arithmetic inlines through the call chain.
  • add_dword_in_place / sub_dword_in_place: convert bool carry from overflowing_add to Word via Word::from().

Callers (integer/src/{mul,sqr}/simple.rs)

  • add_mul_chunk / sub_mul_chunk / squaring: carry/borrow variables changed from bool to Word.

Benchmarks (Apple M-series, non-throttled)

ubig_add

  ┌───────┬──────────┬──────────┬────────┐
  │ size  │  master  │ this PR  │ change │
  ├───────┼──────────┼──────────┼────────┤
  │ 100   │ 1.64 ns  │ 1.64 ns  │ —      │
  ├───────┼──────────┼──────────┼────────┤
  │ 1000  │ 31.9 ns  │ 31.6 ns  │ —      │
  ├───────┼──────────┼──────────┼────────┤
  │ 10000 │ 220.5 ns │ 138.7 ns │ −37%   │
  └───────┴──────────┴──────────┴────────┘

At 10000-bit, dashu now beats malachite (140.5 ns).

ubig_sub

  ┌───────┬──────────┬──────────┬────────┐
  │ size  │  master  │ this PR  │ change │
  ├───────┼──────────┼──────────┼────────┤
  │ 100   │ 1.69 ns  │ 1.67 ns  │ —      │
  ├───────┼──────────┼──────────┼────────┤
  │ 1000  │ 30.5 ns  │ 29.8 ns  │ —      │
  ├───────┼──────────┼──────────┼────────┤
  │ 10000 │ 176.3 ns │ 144.2 ns │ −18%   │
  └───────┴──────────┴──────────┴────────┘

ubig_mul

  ┌─────────┬──────────┬──────────┬────────┐
  │  size   │  master  │ this PR  │ change │
  ├─────────┼──────────┼──────────┼────────┤
  │ 100     │ 20.0 ns  │ 18.7 ns  │ −7%    │
  ├─────────┼──────────┼──────────┼────────┤
  │ 1000    │ 245.8 ns │ 249.9 ns │ —      │
  ├─────────┼──────────┼──────────┼────────┤
  │ 10000   │ 12.61 µs │ 11.74 µs │ −7%    │
  ├─────────┼──────────┼──────────┼────────┤
  │ 100000  │ 451.9 µs │ 412.2 µs │ −9%    │
  ├─────────┼──────────┼──────────┼────────┤
  │ 1000000 │ 14.83 ms │ 12.08 ms │ −19%   │
  └─────────┴──────────┴──────────┴────────┘

At 100/1000 bits the values are inlined (1–2 words, or ~16 words where dispatch overhead dominates the inner loop), so the carry-chain change has negligible effect. The benefit
grows with operand size as the inner loop becomes the dominant cost.

@cmpute
cmpute merged commit 5d89eda into master Jun 11, 2026
13 checks passed
@cmpute
cmpute deleted the word-carry branch June 11, 2026 16:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant