Remove a source of O(n^2) running time in bigints.

::num::bigint, Remove a source of O(n^2) running time in `fn shr_bits`.

I'll cut to the chase: On my laptop, this brings the running time on
`pidigits 2000` (from src/test/bench/shootout-pidigits.rs) from this:
```
% time ./pidigits 2000 > /dev/null

real	0m7.695s
user	0m7.690s
sys	0m0.005s
```
to this:
```
% time ./pidigits 2000 > /dev/null

real	0m0.322s
user	0m0.318s
sys	0m0.004s
```

The previous code was building up a vector by repeatedly making a
fresh copy for each element that was unshifted onto the front,
yielding quadratic running time.  This fixes that by building up the
vector in reverse order (pushing elements onto the end) and then
reversing it.

(Another option would be to build up a zero-initialized vector of the
desired length and then installing all of the shifted result elements
into their target index, but this was easier to hack up quickly, and
yields the desired asymptotic improvement.  I have been thinking of
adding a `vec::from_fn_rev` to handle this case, maybe I will try that
this weekend.)
This commit is contained in:
Felix S. Klock II 2014-02-09 23:13:47 +01:00 committed by Alex Crichton
parent 606c23a789
commit 7dc187afd8

View File

@ -784,11 +784,12 @@ fn shr_bits(&self, n_bits: uint) -> BigUint {
if n_bits == 0 || self.data.is_empty() { return (*self).clone(); }
let mut borrow = 0;
let mut shifted = ~[];
let mut shifted_rev = vec::with_capacity(self.data.len());
for elem in self.data.rev_iter() {
shifted = ~[(*elem >> n_bits) | borrow] + shifted;
shifted_rev.push((*elem >> n_bits) | borrow);
borrow = *elem << (BigDigit::bits - n_bits);
}
let shifted = { shifted_rev.reverse(); shifted_rev };
return BigUint::new(shifted);
}
@ -2637,4 +2638,15 @@ fn to_str(bh: &mut BenchHarness) {
fib.to_str();
});
}
#[bench]
fn shr(bh: &mut BenchHarness) {
let n = { let one : BigUint = One::one(); one << 1000 };
bh.iter(|| {
let mut m = n.clone();
for _ in range(0, 10) {
m = m >> 1;
}
})
}
}