Skip to content

Commit

Permalink
Binomial: Use integer exponentation
Browse files Browse the repository at this point in the history
This increase performance significantly, see below. However, it requires
shipping our own integer exponentiation implementation, because `u64`
exponents are not supported by `std` or `num`.

Before:
```
test distr_binomial       ... bench:  88,365 ns/iter (+/- 2,215) =90 MB/s
test distr_binomial_small ... bench:  33,775 ns/iter (+/- 5,494) =236 MB/s
test misc_binomial_1      ... bench:      13 ns/iter (+/- 2)
test misc_binomial_10     ... bench:      72 ns/iter (+/- 1)
test misc_binomial_100    ... bench:      71 ns/iter (+/- 11)
test misc_binomial_1000   ... bench:     624 ns/iter (+/- 19)
test misc_binomial_1e12   ... bench:     681 ns/iter (+/- 18)
```

After:
```
test distr_binomial       ... bench:  44,354 ns/iter (+/- 3,518) = 180 MB/s
test distr_binomial_small ... bench:  22,736 ns/iter (+/- 514) = 351 MB/s
test misc_binomial_1      ... bench:      10 ns/iter (+/- 0)
test misc_binomial_10     ... bench:      23 ns/iter (+/- 0)
test misc_binomial_100    ... bench:      27 ns/iter (+/- 4)
test misc_binomial_1000   ... bench:     621 ns/iter (+/- 15)
test misc_binomial_1e12   ... bench:     686 ns/iter (+/- 20)
```
  • Loading branch information
vks committed Feb 26, 2019
1 parent c360c6c commit d23c704
Showing 1 changed file with 30 additions and 3 deletions.
33 changes: 30 additions & 3 deletions src/distributions/binomial.rs
Expand Up @@ -47,6 +47,35 @@ impl Binomial {
}
}

/// Raise a `base` to the power of `exp`, using exponentiation by squaring.
///
/// This implementation is based on the one in the `num_traits` crate. It is
/// slightly modified to accept `u64` exponents.
fn pow(mut base: f64, mut exp: u64) -> f64 {
if exp == 0 {
return 1.;
}

while exp & 1 == 0 {
base *= base;
exp >>= 1;
}

if exp == 1 {
return base;
}

let mut acc = base;
while exp > 1 {
exp >>= 1;
base *= base;
if exp & 1 == 1 {
acc *= base;
}
}
acc
}

impl Distribution<u64> for Binomial {
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> u64 {
// Handle these values directly.
Expand Down Expand Up @@ -77,9 +106,7 @@ impl Distribution<u64> for Binomial {
let q = 1. - p;
let s = p / q;
let a = ((self.n + 1) as f64) * s;
let mut r = q.powf(self.n as f64);
// FIXME: Using integer exponentiation should be faster, but it is not available for
// `u64` exponents, neither in `std` or `num`.
let mut r = pow(q, self.n);
let mut u: f64 = rng.gen();
let mut x = 0;
while u > r as f64 {
Expand Down

0 comments on commit d23c704

Please sign in to comment.