Skip to content

Commit

Permalink
Auto merge of serde-rs#205 - cuviper:bigint-sub, r=hauleth
Browse files Browse the repository at this point in the history
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 serde-rs#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)
  • Loading branch information
homu committed Jul 10, 2016
2 parents a7ac5e4 + 388a313 commit 7fcd5f7
Show file tree
Hide file tree
Showing 2 changed files with 64 additions and 1 deletion.
28 changes: 28 additions & 0 deletions benches/bigint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand All @@ -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);
Expand Down Expand Up @@ -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);
Expand Down
37 changes: 36 additions & 1 deletion bigint/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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)];
Expand Down

0 comments on commit 7fcd5f7

Please sign in to comment.