bigint: allow `Sub` to work in-place on the RHS

A new Fibonacci benchmark demonstrates the improvement by using both
addition and subtraction in each iteration of the loop, like #200.

Before:

    test fib2_100          ... bench:       4,558 ns/iter (+/- 3,357)
    test fib2_1000         ... bench:      62,575 ns/iter (+/- 5,200)
    test fib2_10000        ... bench:   2,898,425 ns/iter (+/- 207,973)

After:

    test fib2_100          ... bench:       1,973 ns/iter (+/- 102)
    test fib2_1000         ... bench:      41,203 ns/iter (+/- 947)
    test fib2_10000        ... bench:   2,544,272 ns/iter (+/- 45,183)
This commit is contained in:
Josh Stone 2016-07-08 17:34:12 -07:00
parent a7ac5e4299
commit 388a3132b8
2 changed files with 64 additions and 1 deletions

View File

@ -40,6 +40,7 @@ fn factorial(n: usize) -> BigUint {
f
}
/// Compute Fibonacci numbers
fn fib(n: usize) -> BigUint {
let mut f0: BigUint = Zero::zero();
let mut f1: BigUint = One::one();
@ -50,6 +51,18 @@ fn fib(n: usize) -> BigUint {
f0
}
/// Compute Fibonacci numbers with two ops per iteration
/// (add and subtract, like issue #200)
fn fib2(n: usize) -> BigUint {
let mut f0: BigUint = Zero::zero();
let mut f1: BigUint = One::one();
for _ in 0..n {
f1 = f1 + &f0;
f0 = &f1 - f0;
}
f0
}
#[bench]
fn multiply_0(b: &mut Bencher) {
multiply_bench(b, 1 << 8, 1 << 8);
@ -100,6 +113,21 @@ fn fib_10000(b: &mut Bencher) {
b.iter(|| fib(10000));
}
#[bench]
fn fib2_100(b: &mut Bencher) {
b.iter(|| fib2(100));
}
#[bench]
fn fib2_1000(b: &mut Bencher) {
b.iter(|| fib2(1000));
}
#[bench]
fn fib2_10000(b: &mut Bencher) {
b.iter(|| fib2(10000));
}
#[bench]
fn fac_to_string(b: &mut Bencher) {
let fac = factorial(100);

View File

@ -827,7 +827,8 @@ impl<'a> Add<&'a BigUint> for BigUint {
}
}
forward_all_binop_to_val_ref!(impl Sub for BigUint, sub);
forward_val_val_binop!(impl Sub for BigUint, sub);
forward_ref_ref_binop!(impl Sub for BigUint, sub);
fn sub2(a: &mut [BigDigit], b: &[BigDigit]) {
let mut borrow = 0;
@ -861,6 +862,40 @@ impl<'a> Sub<&'a BigUint> for BigUint {
}
}
fn sub2rev(a: &[BigDigit], b: &mut [BigDigit]) {
debug_assert!(b.len() >= a.len());
let mut borrow = 0;
let len = cmp::min(a.len(), b.len());
let (a_lo, a_hi) = a.split_at(len);
let (b_lo, b_hi) = b.split_at_mut(len);
for (a, b) in a_lo.iter().zip(b_lo) {
*b = sbb(*a, *b, &mut borrow);
}
assert!(a_hi.is_empty());
// note: we're _required_ to fail on underflow
assert!(borrow == 0 && b_hi.iter().all(|x| *x == 0),
"Cannot subtract b from a because b is larger than a.");
}
impl<'a> Sub<BigUint> for &'a BigUint {
type Output = BigUint;
fn sub(self, mut other: BigUint) -> BigUint {
if other.data.len() < self.data.len() {
let extra = self.data.len() - other.data.len();
other.data.extend(repeat(0).take(extra));
}
sub2rev(&self.data[..], &mut other.data[..]);
other.normalize()
}
}
fn sub_sign(a: &[BigDigit], b: &[BigDigit]) -> BigInt {
// Normalize:
let a = &a[..a.iter().rposition(|&x| x != 0).map_or(0, |i| i + 1)];