Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Speed up range sampling. #115

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions benches/distributions/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
mod exponential;
mod normal;
mod gamma;
mod range;
18 changes: 18 additions & 0 deletions benches/distributions/range.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
use std::mem::size_of;
use std::cmp;
use test::Bencher;
use rand;
use rand::distributions::Sample;
use rand::distributions::Range;

#[bench]
fn rand_range(b: &mut Bencher) {
let mut rng = rand::weak_rng();
let mut range = Range::new(10, 10000);
b.iter(|| {
for _ in 0..::RAND_BENCH_N {
range.sample(&mut rng);
}
});
b.bytes = size_of::<u32>() as u64 * ::RAND_BENCH_N;
}
27 changes: 9 additions & 18 deletions src/distributions/range.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ use distributions::{Sample, IndependentSample};
pub struct Range<X> {
low: X,
range: X,
accept_zone: X
lz: X,
}

impl<X: SampleRange + PartialOrd> Range<X> {
Expand Down Expand Up @@ -98,31 +98,22 @@ macro_rules! integer_impl {

fn construct_range(low: $ty, high: $ty) -> Range<$ty> {
let range = (w(high as $unsigned) - w(low as $unsigned)).0;
let unsigned_max: $unsigned = ::std::$unsigned::MAX;

// this is the largest number that fits into $unsigned
// that `range` divides evenly, so, if we've sampled
// `n` uniformly from this region, then `n % range` is
// uniform in [0, range)
let zone = unsigned_max - unsigned_max % range;

Range {
low: low,
range: range as $ty,
accept_zone: zone as $ty
lz: range.leading_zeros() as $ty,
}
}
#[inline]
fn sample_range<R: Rng>(r: &Range<$ty>, rng: &mut R) -> $ty {
loop {
// rejection sample
let v = rng.gen::<$unsigned>();
// until we find something that fits into the
// region which r.range evenly divides (this will
// be uniformly distributed)
if v < r.accept_zone as $unsigned {
// and return it, with some adjustments
return (w(r.low) + w((v % r.range as $unsigned) as $ty)).0;
// rejection sample. Slough off the low bits so the maximum
// generated value is the next largest power of 2.
let v = rng.gen::<$unsigned>() >> r.lz;
if v < r.range as $unsigned {
// offset the value onto our range.
return (w(r.low) + w((v as $unsigned) as $ty)).0;
}
}
}
Expand All @@ -148,7 +139,7 @@ macro_rules! float_impl {
Range {
low: low,
range: high - low,
accept_zone: 0.0 // unused
lz: 0.0,
}
}
fn sample_range<R: Rng>(r: &Range<$ty>, rng: &mut R) -> $ty {
Expand Down