-
Notifications
You must be signed in to change notification settings - Fork 152
perf(bigint): speed up large BigInt x small scalar multiplication #3620
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
2292395
ada88f8
f57187e
451a442
074616a
de08ec0
4a5fc0b
d580abe
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -384,14 +384,48 @@ pub impl Mul for BigInt with fn mul(self : BigInt, other : BigInt) -> BigInt { | |
| if self.is_zero() || other.is_zero() { | ||
| return zero | ||
| } | ||
| let ret = if self.len < karatsuba_threshold || other.len < karatsuba_threshold { | ||
| // Specialize the (n-limb) x (1-limb) case. Factorial-style chains hit | ||
| // this on every multiplication, and the general grade-school loop has | ||
| // a per-i branch on `j < other_len` plus carry propagation overhead | ||
| // that disappears here. | ||
| let ret = if other.len == 1 { | ||
| self.mul_single_limb(other.limbs[0]) | ||
| } else if self.len == 1 { | ||
| other.mul_single_limb(self.limbs[0]) | ||
| } else if self.len < karatsuba_threshold || other.len < karatsuba_threshold { | ||
| self.grade_school_mul(other) | ||
| } else { | ||
| self.karatsuba_mul(other) | ||
| } | ||
| { ..ret, sign: if self.sign == other.sign { Positive } else { Negative } } | ||
| } | ||
|
|
||
| ///| | ||
| /// Multiply the magnitude of `self` by a single radix-limb `x`. Returns | ||
| /// a `Positive`-signed result regardless of `self.sign` — the caller in | ||
| /// `Mul::mul` overwrites `sign` with the correct combined sign. This | ||
| /// matches the convention of the other magnitude-only multiply helpers | ||
| /// (`grade_school_mul`, `karatsuba_mul`). Do not call directly when a | ||
| /// signed product is needed. | ||
| fn BigInt::mul_single_limb(self : BigInt, x : UInt) -> BigInt { | ||
| let n = self.len | ||
| let limbs = FixedArray::make(n + 1, 0U) | ||
| let xq = x.to_uint64() | ||
| let mut carry = 0UL | ||
| for i in 0..<n { | ||
| let product = self.limbs[i].to_uint64() * xq + carry | ||
| limbs[i] = (product & radix_mask).to_uint() | ||
| carry = product >> radix_bit_len | ||
| } | ||
| let len = if carry == 0UL { | ||
| n | ||
| } else { | ||
| limbs[n] = carry.to_uint() | ||
| n + 1 | ||
| } | ||
| { limbs, sign: Positive, len } | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Documented in ada88f8. The magnitude-only contract here mirrors |
||
| } | ||
|
|
||
| // Simplest way to multiply two BigInts. | ||
|
|
||
| ///| | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
radix_maskandradix_bit_lenare already in the right types:let radix_mask : UInt64 = radix - 1(line 89) andlet radix_bit_len = 32(line 81,Int, which is the expected shift-width type forUInt64 >> Intin moonbit). Soproduct & radix_maskisUInt64 & UInt64andproduct >> radix_bit_lenisUInt64 >> Int, no implicit conversion. The existinggrade_school_mul,karatsuba_mul, anddivhelpers in this same file all use these constants the same way (see e.g. lines 595, 597, 670–688), so the new code follows established convention and stays in sync if those constants are ever retyped.