Skip to content

Commit

Permalink
Merge branch 'reseeding_perf' into hc-128
Browse files Browse the repository at this point in the history
  • Loading branch information
pitdicker committed Dec 16, 2017
2 parents 66e5d15 + 0fad9f5 commit 2448e21
Show file tree
Hide file tree
Showing 16 changed files with 438 additions and 303 deletions.
4 changes: 3 additions & 1 deletion benches/distributions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,11 +75,13 @@ macro_rules! gen_range_int {
#[bench]
fn $fnn(b: &mut Bencher) {
let mut rng = XorShiftRng::new().unwrap();
let high = $high;

b.iter(|| {
for _ in 0..::RAND_BENCH_N {
let x: $ty = Range::new($low, $high).sample(&mut rng);
let x: $ty = Range::sample_single($low, high, &mut rng);
black_box(x);
black_box(high);
}
});
b.bytes = size_of::<$ty>() as u64 * ::RAND_BENCH_N;
Expand Down
73 changes: 72 additions & 1 deletion benches/generators.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,9 @@ const BYTES_LEN: usize = 1024;
use std::mem::size_of;
use test::{black_box, Bencher};

use rand::{Rng, NewSeeded, Sample, SeedFromRng, StdRng, OsRng, JitterRng};
use rand::{Rng, NewSeeded, Sample, SeedFromRng, StdRng, OsRng, JitterRng, thread_rng};
use rand::prng::*;
use rand::reseeding::{ReseedingRng, ReseedWithNew};

macro_rules! gen_bytes {
($fnn:ident, $gen:ident) => {
Expand Down Expand Up @@ -102,3 +103,73 @@ fn init_jitter(b: &mut Bencher) {
black_box(JitterRng::new().unwrap());
});
}



#[bench]
fn reseeding_xorshift_bytes(b: &mut Bencher) {
let mut rng = ReseedingRng::new(XorShiftRng::new().unwrap(),
128*1024*1024*1024,
ReseedWithNew);
let mut buf = [0u8; BYTES_LEN];
b.iter(|| {
for _ in 0..RAND_BENCH_N {
rng.fill_bytes(&mut buf);
black_box(buf);
}
});
b.bytes = BYTES_LEN as u64 * RAND_BENCH_N;
}

macro_rules! reseeding_uint {
($fnn:ident, $ty:ty) => {
#[bench]
fn $fnn(b: &mut Bencher) {
let mut rng = ReseedingRng::new(XorShiftRng::new().unwrap(),
128*1024*1024*1024,
ReseedWithNew);
b.iter(|| {
for _ in 0..RAND_BENCH_N {
black_box(rng.gen::<$ty>());
}
});
b.bytes = size_of::<$ty>() as u64 * RAND_BENCH_N;
}
}
}

reseeding_uint!(reseeding_xorshift_u32, u32);
reseeding_uint!(reseeding_xorshift_u64, u64);



#[bench]
fn gen_bytes_threadrng(b: &mut Bencher) {
let mut rng = thread_rng();
let mut buf = [0u8; BYTES_LEN];
b.iter(|| {
for _ in 0..RAND_BENCH_N {
rng.fill_bytes(&mut buf);
black_box(buf);
}
});
b.bytes = BYTES_LEN as u64 * RAND_BENCH_N;
}

macro_rules! reseeding_uint {
($fnn:ident, $ty:ty) => {
#[bench]
fn $fnn(b: &mut Bencher) {
let mut rng = thread_rng();
b.iter(|| {
for _ in 0..RAND_BENCH_N {
black_box(rng.gen::<$ty>());
}
});
b.bytes = size_of::<$ty>() as u64 * RAND_BENCH_N;
}
}
}

reseeding_uint!(gen_u32_threadrng, u32);
reseeding_uint!(gen_u64_threadrng, u64);
44 changes: 19 additions & 25 deletions src/distributions/default.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@
//! Generic value creation

use Rng;
use distributions::{Distribution, Rand};
use distributions::uniform::{Uniform, Uniform01, codepoint};
use distributions::Distribution;
use distributions::uniform::{Uniform, Uniform01, Codepoint};

/// A generic random value distribution. Generates values using what appears to
/// be "the best" distribution for each type, but ultimately the choice is arbitrary.
Expand All @@ -21,67 +21,61 @@ use distributions::uniform::{Uniform, Uniform01, codepoint};
///
/// * [`Uniform`] for integer types
/// * [`Uniform01`] for floating point types
///
/// Makes use of the following methods:
///
/// * [`codepoint`] for `char`
/// * [`Codepoint`] for `char`
///
/// TODO: link
#[derive(Debug)]
pub struct Default;

// ----- implementations -----

impl<T: Rand<Uniform>> Distribution<T> for Default {
impl<T> Distribution<T> for Default where Uniform: Distribution<T>{
fn sample<R: Rng+?Sized>(&self, rng: &mut R) -> T {
T::rand(rng, Uniform)
Uniform.sample(rng)
}
}

// FIXME: https://github.com/rust-lang/rust/issues/23341
// impl<T: Rand<Uniform01>> Distribution<T> for Default {
// impl<T> Distribution<T> for Default where Uniform01: Distribution<T>{
// fn sample<R: Rng+?Sized>(&self, rng: &mut R) -> T {
// T::rand(rng, Uniform01)
// Uniform01.sample(rng)
// }
// }
// workaround above issue:
impl Distribution<f32> for Default {
fn sample<R: Rng+?Sized>(&self, rng: &mut R) -> f32 {
f32::rand(rng, Uniform01)
Uniform01.sample(rng)
}
}
impl Distribution<f64> for Default {
fn sample<R: Rng+?Sized>(&self, rng: &mut R) -> f64 {
f64::rand(rng, Uniform01)
Uniform01.sample(rng)
}
}

impl Distribution<char> for Default {
fn sample<R: Rng+?Sized>(&self, rng: &mut R) -> char {
codepoint(rng)
Codepoint.sample(rng)
}
}


#[cfg(test)]
mod tests {
use {Rng, thread_rng};
use distributions::{Rand, Default};
use {Rng, Sample, thread_rng};
use distributions::{Default};

#[test]
fn test_types() {
let rng: &mut Rng = &mut thread_rng();
fn do_test<T: Rand<Default>>(rng: &mut Rng) -> T {
T::rand(rng, Default)
}

do_test::<u32>(rng);
do_test::<i8>(rng);
do_test::<f32>(rng);
do_test::<f64>(rng);
rng.sample::<u32, _>(Default);
rng.sample::<i8, _>(Default);
rng.sample::<f32, _>(Default);
rng.sample::<f64, _>(Default);
#[cfg(feature = "i128_support")]
do_test::<u128>(rng);
do_test::<char>(rng);
do_test::<bool>(rng);
rng.sample::<u128, _>(Default);
rng.sample::<char, _>(Default);
rng.sample::<bool, _>(Default);
}
}
4 changes: 2 additions & 2 deletions src/distributions/exponential.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
//! The exponential distribution.

use {Rng};
use distributions::{ziggurat, ziggurat_tables, Distribution, Uniform01, Rand};
use distributions::{ziggurat, ziggurat_tables, Distribution, Uniform01};

/// Generates Exp(1) random numbers.
///
Expand Down Expand Up @@ -43,7 +43,7 @@ pub fn exp1<R: Rng+?Sized>(rng: &mut R) -> f64 {
}
#[inline]
fn zero_case<R:Rng+?Sized>(rng: &mut R, _u: f64) -> f64 {
let x = f64::rand(rng, Uniform01);
let x: f64 = Uniform01.sample(rng);
ziggurat_tables::ZIG_EXP_R - x.ln()
}

Expand Down
6 changes: 3 additions & 3 deletions src/distributions/gamma.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ use self::ChiSquaredRepr::*;

use {Rng};
use distributions::normal::standard_normal;
use distributions::{Distribution, Exp, Rand, Open01};
use distributions::{Distribution, Exp, Open01};

/// The Gamma distribution `Gamma(shape, scale)` distribution.
///
Expand Down Expand Up @@ -145,7 +145,7 @@ impl Distribution<f64> for Gamma {
}
impl Distribution<f64> for GammaSmallShape {
fn sample<R: Rng+?Sized>(&self, rng: &mut R) -> f64 {
let u = f64::rand(rng, Open01);
let u: f64 = Open01.sample(rng);

self.large_shape.sample(rng) * u.powf(self.inv_shape)
}
Expand All @@ -160,7 +160,7 @@ impl Distribution<f64> for GammaLargeShape {
}

let v = v_cbrt * v_cbrt * v_cbrt;
let u = f64::rand(rng, Open01);
let u: f64 = Open01.sample(rng);

let x_sqr = x * x;
if u < 1.0 - 0.0331 * x_sqr * x_sqr ||
Expand Down
58 changes: 13 additions & 45 deletions src/distributions/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,11 @@
//! generated values; for example `Range` needs to know its upper and lower
//! bounds. Distributions use the `Distribution` trait to yield values: call
//! `distr.sample(&mut rng)` to get a random variable.
//!
//! TODO: is it worth exposing both submodules and re-exporting their members?

use Rng;

pub use self::default::Default;
pub use self::uniform::{uniform, codepoint, ascii_word_char};
pub use self::uniform::{Uniform, Uniform01, Open01, Closed01, AsciiWordChar};
pub use self::uniform::{Uniform, Uniform01, Open01, Closed01, Codepoint, AsciiWordChar};
pub use self::range::Range;

#[cfg(feature="std")]
Expand Down Expand Up @@ -79,55 +76,26 @@ impl<'a, T, D: Distribution<T>> Distribution<T> for &'a D {
}
}

/// Generic trait for sampling random values from some distribution
/// Trait for sampling values from the `Default` distribution.
///
/// TODO: quite possibly remove both this and `SimpleRand` since `Sample` is
/// more convenient and distributions like `Default` handle all the real work.
/// This is mostly a shim around `Default` for backwards compatibility; the
/// implementation is simply `Default.sample(rng)`.
///
/// # Example
/// ```rust
/// use rand::distributions::{Rand, Default, Range};
/// use rand::{Rand, thread_rng};
///
/// let mut rng = rand::thread_rng();
/// println!("Random byte: {}", u8::rand(&mut rng, Default));
/// println!("Random range: {}", i32::rand(&mut rng, Range::new(-99, 100)));
/// ```
pub trait Rand<D> {
/// Generate a random value of the given type, according to the specified
/// distribution.
///
/// The distributions `Default` (or `Uniform` and `Uniform01`) and `Range`
/// should cover most simpler usages; `Normal`, `LogNormal`, `Exp`, `Gamma`
/// and a few others are also available.
fn rand<R: Rng+?Sized>(rng: &mut R, distr: D) -> Self;
}

impl<T, D: Distribution<T>> Rand<D> for T {
fn rand<R: Rng+?Sized>(rng: &mut R, distr: D) -> Self {
distr.sample(rng)
}
}

/// Simpler version of `Rand`, without support for alternative distributions.
///
/// TODO: decide which version of `Rand` to keep. If this one, rename struct to
/// `Rand` and function to `rand`.
///
/// # Example
/// ```rust
/// use rand::distributions::SimpleRand;
///
/// let mut rng = rand::thread_rng();
/// println!("Random byte: {}", u8::simple_rand(&mut rng));
/// let mut rng = thread_rng();
/// println!("Random byte: {}", u8::rand(&mut rng));
/// ```
pub trait SimpleRand {
pub trait Rand : Sized {
/// Generate a random value of the given type, using the `Default`
/// distribution.
fn simple_rand<R: Rng+?Sized>(rng: &mut R) -> Self;
fn rand<R: Rng+?Sized>(rng: &mut R) -> Self;
}

impl<T> SimpleRand for T where Default: Distribution<T> {
fn simple_rand<R: Rng+?Sized>(rng: &mut R) -> Self {
impl<T> Rand for T where Default: Distribution<T> {
fn rand<R: Rng+?Sized>(rng: &mut R) -> Self {
Default.sample(rng)
}
}
Expand Down Expand Up @@ -167,7 +135,7 @@ fn ziggurat<R: Rng+?Sized, P, Z>(
// precision of using 64 bits for f64 does not buy us much.
// Because for some RNG's the least significant bits can be of lower
// statistical quality, we use bits 3..10 for i.
let bits: u64 = uniform(rng);
let bits: u64 = rng.sample(Uniform);

// u is either U(-1, 1) or U(0, 1) depending on if this is a
// symmetric distribution or not.
Expand All @@ -192,7 +160,7 @@ fn ziggurat<R: Rng+?Sized, P, Z>(
return zero_case(rng, u);
}
// algebraically equivalent to f1 + DRanU()*(f0 - f1) < 1
if f_tab[i + 1] + (f_tab[i] - f_tab[i + 1]) * f64::rand(rng, Uniform01) < pdf(x) {
if f_tab[i + 1] + (f_tab[i] - f_tab[i + 1]) * rng.sample::<f64, _>(Uniform01) < pdf(x) {
return x;
}
}
Expand Down
6 changes: 3 additions & 3 deletions src/distributions/normal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
//! The normal and derived distributions.

use {Rng};
use distributions::{ziggurat, ziggurat_tables, Distribution, Rand, Open01};
use distributions::{ziggurat, ziggurat_tables, Distribution, Open01};

/// Generates N(0, 1) random numbers
/// (a.k.a. a standard normal, or Gaussian).
Expand Down Expand Up @@ -50,8 +50,8 @@ pub fn standard_normal<R:Rng+?Sized>(rng: &mut R) -> f64 {
let mut y = 0.0f64;

while -2.0 * y < x * x {
let x_ = f64::rand(rng, Open01);
let y_ = f64::rand(rng, Open01);
let x_: f64 = Open01.sample(rng);
let y_: f64 = Open01.sample(rng);

x = x_.ln() / ziggurat_tables::ZIG_NORM_R;
y = y_.ln();
Expand Down
Loading

0 comments on commit 2448e21

Please sign in to comment.