From f44bd0aee2d9b3604646af6ad80674ccc028fb9e Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Mon, 14 May 2018 11:23:12 -0700 Subject: [PATCH 1/3] Add a special case for single-digit divisors It was pointed out in the blog post [Big Integers in Zig] that we don't have a special case in `num-bigint` for single-digit divisors. While you can already get this optimization by dividing directly by `u32`, it's easy to make small `BigUint` divisors work like this too. $ cargo benchcmp baseline single-div name baseline ns/iter single-div ns/iter diff ns/iter diff % speedup factorial_div_biguint 5,638,353 1,005,488 -4,632,865 -82.17% x 5.61 `BigInt` will also gain from this, since it uses `BigUint` division internally. Running [zig-bn's facdiv-rs] shows a nice improvement too. My i7-7700K with Rust 1.26 goes from 4.15 seconds to just 0.65 -- a 6.38x speedup! [Big Integers in Zig]: https://tiehuis.github.io/big-integers-in-zig#division-test-single-limb [zig-bn's facdiv-rs]: https://github.com/tiehuis/zig-bn/tree/master/bench/facdiv/crate-facdiv-rs --- Cargo.toml | 3 +++ benches/factorial.rs | 35 +++++++++++++++++++++++++++++++++++ src/algorithms.rs | 6 +++++- 3 files changed, 43 insertions(+), 1 deletion(-) create mode 100755 benches/factorial.rs diff --git a/Cargo.toml b/Cargo.toml index 46c5965..5da5be6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,6 +14,9 @@ readme = "README.md" [[bench]] name = "bigint" +[[bench]] +name = "factorial" + [[bench]] name = "gcd" diff --git a/benches/factorial.rs b/benches/factorial.rs new file mode 100755 index 0000000..fc79845 --- /dev/null +++ b/benches/factorial.rs @@ -0,0 +1,35 @@ +#![feature(test)] + +extern crate num_bigint; +extern crate num_traits; +extern crate test; + +use num_bigint::BigUint; +use num_traits::One; +use std::ops::{Div, Mul}; +use test::Bencher; + +#[bench] +fn factorial_mul_biguint(b: &mut Bencher) { + b.iter(|| (1u32..1000).map(BigUint::from).fold(BigUint::one(), Mul::mul)); +} + +#[bench] +fn factorial_mul_u32(b: &mut Bencher) { + b.iter(|| (1u32..1000).fold(BigUint::one(), Mul::mul)); +} + +// The division test is inspired by this blog comparison: +// + +#[bench] +fn factorial_div_biguint(b: &mut Bencher) { + let n: BigUint = (1u32..1000).fold(BigUint::one(), Mul::mul); + b.iter(|| (1u32..1000).rev().map(BigUint::from).fold(n.clone(), Div::div)); +} + +#[bench] +fn factorial_div_u32(b: &mut Bencher) { + let n: BigUint = (1u32..1000).fold(BigUint::one(), Mul::mul); + b.iter(|| (1u32..1000).rev().fold(n.clone(), Div::div)); +} diff --git a/src/algorithms.rs b/src/algorithms.rs index e65a778..d40bbc1 100644 --- a/src/algorithms.rs +++ b/src/algorithms.rs @@ -456,9 +456,13 @@ pub fn div_rem(u: &BigUint, d: &BigUint) -> (BigUint, BigUint) { if u.is_zero() { return (Zero::zero(), Zero::zero()); } - if *d == One::one() { + if d.data == [1] { return (u.clone(), Zero::zero()); } + if d.data.len() == 1 { + let (div, rem) = div_rem_digit(u.clone(), d.data[0]); + return (div, rem.into()); + } // Required or the q_len calculation below can underflow: match u.cmp(d) { From 10e00ff66ad5adbd5bfca418a1fdc67551f599ee Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Mon, 14 May 2018 11:23:41 -0700 Subject: [PATCH 2/3] Release 0.1.44 --- Cargo.toml | 2 +- RELEASES.md | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 5da5be6..deb5a4b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,7 +8,7 @@ categories = [ "algorithms", "data-structures", "science" ] license = "MIT/Apache-2.0" name = "num-bigint" repository = "https://github.com/rust-num/num-bigint" -version = "0.1.43" +version = "0.1.44" readme = "README.md" [[bench]] diff --git a/RELEASES.md b/RELEASES.md index 1bf8b08..023bc6e 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -1,3 +1,11 @@ +# Release 0.1.44 + +- [Division with single-digit divisors is now much faster.][42] + +**Contributors**: @cuviper + +[42]: https://github.com/rust-num/num-bigint/pull/42 + # Release 0.1.43 - [The new `BigInt::modpow`][18] performs signed modular exponentiation, using From 2d37c0bb1012d476a2a9ae1433a3e28f9f9ae2ca Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Mon, 14 May 2018 12:06:50 -0700 Subject: [PATCH 3/3] Mention the crate comparisons in 0.1.44 too ... if only to give more complete author credits for the release. --- RELEASES.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/RELEASES.md b/RELEASES.md index 023bc6e..c549993 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -1,9 +1,12 @@ # Release 0.1.44 - [Division with single-digit divisors is now much faster.][42] +- The README now compares [`ramp`, `rug`, `rust-gmp`][20], and [`apint`][21]. -**Contributors**: @cuviper +**Contributors**: @cuviper, @Robbepop +[20]: https://github.com/rust-num/num-bigint/pull/20 +[21]: https://github.com/rust-num/num-bigint/pull/21 [42]: https://github.com/rust-num/num-bigint/pull/42 # Release 0.1.43