Skip to content

Deprecate random and weak_rng, add SmallRng #296

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

Merged
merged 5 commits into from
Mar 13, 2018
Merged
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
12 changes: 6 additions & 6 deletions benches/misc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@ extern crate rand;

use test::{black_box, Bencher};

use rand::{Rng, weak_rng};
use rand::{SeedableRng, SmallRng, Rng, thread_rng};
use rand::seq::*;

#[bench]
fn misc_shuffle_100(b: &mut Bencher) {
let mut rng = weak_rng();
let mut rng = SmallRng::from_rng(&mut thread_rng()).unwrap();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is really not very ergonomic; we've gone from 1 identifier to 4 with lots of extra syntax too. Any better ideas?

Error handling is pedantic because thread_rng will almost never fail, but we can't really be removed from from_rng.

We could allow something like thread_rng().new_rng::<SmallRng>() but it's not a lot better and adds a function to Rng which doesn't have many other uses.

We could just rename weak_rng to small_rng. Not sure.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure it's an issue outside of small examples. Usually you want to reuse thread_rng(). Maybe something like the following makes it more clear in the examples:

// Create a big, expensive to initialize and slower, but unpredictable RNG.
// This automatically done only once per thread.
let mut thread_rng = thread_rng();
// Create small, cheap to initialize and fast RNG with a random seed.
// This is very unlikely to fail.
let mut small_rng = SmallRng::from_rng(&mut thread_rng).unwrap();

This is even more verbose, but it teaches the correct pattern.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's also wrong: thread_rng() is only slow on the first call per thread. SmallRng::new() would be faster if thread_rng was never otherwise used — but chances are good that it would be used somewhere, I guess (at least once std's hash function randomisation uses this, not its own version).

Yes, copy-pasting this is fine, but often people don't look up the doc, they just vaguely remember how to do something and think, ah, SmallRng::new() should work.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's also wrong: thread_rng() is only slow on the first call per thread.

That's what I was trying to say with "This automatically done only once per thread.", but I guess it could be said more clearly.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any better ideas?

We could implement NewRng for SmallRng such that it uses thread_rng(). However, this requires getting rid of the impl for SeedableRng or specialization, which is not stable yet.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think using NewSeeded everywhere is fine. It is also how things used to be until a few months ago. But it would be nice to mention the thread_rng() method in the documentation just to point out a potential (small) performance advantage. How often will initializing the RNG be a bottleneck?

And I am still not sure thread_rng() is used all that much always. There is std::collections::HashMap that may also use it in the future, and maybe a crates dependency. If it is otherwise not used, spinning up thread_rng for one initialization of SmallRng is wasteful.

let x : &mut [usize] = &mut [1; 100];
b.iter(|| {
rng.shuffle(x);
Expand All @@ -20,7 +20,7 @@ fn misc_shuffle_100(b: &mut Bencher) {

#[bench]
fn misc_sample_iter_10_of_100(b: &mut Bencher) {
let mut rng = weak_rng();
let mut rng = SmallRng::from_rng(&mut thread_rng()).unwrap();
let x : &[usize] = &[1; 100];
b.iter(|| {
black_box(sample_iter(&mut rng, x, 10).unwrap_or_else(|e| e));
Expand All @@ -29,7 +29,7 @@ fn misc_sample_iter_10_of_100(b: &mut Bencher) {

#[bench]
fn misc_sample_slice_10_of_100(b: &mut Bencher) {
let mut rng = weak_rng();
let mut rng = SmallRng::from_rng(&mut thread_rng()).unwrap();
let x : &[usize] = &[1; 100];
b.iter(|| {
black_box(sample_slice(&mut rng, x, 10));
Expand All @@ -38,7 +38,7 @@ fn misc_sample_slice_10_of_100(b: &mut Bencher) {

#[bench]
fn misc_sample_slice_ref_10_of_100(b: &mut Bencher) {
let mut rng = weak_rng();
let mut rng = SmallRng::from_rng(&mut thread_rng()).unwrap();
let x : &[usize] = &[1; 100];
b.iter(|| {
black_box(sample_slice_ref(&mut rng, x, 10));
Expand All @@ -49,7 +49,7 @@ macro_rules! sample_indices {
($name:ident, $amount:expr, $length:expr) => {
#[bench]
fn $name(b: &mut Bencher) {
let mut rng = weak_rng();
let mut rng = SmallRng::from_rng(&mut thread_rng()).unwrap();
b.iter(|| {
black_box(sample_indices(&mut rng, $length, $amount));
})
Expand Down
4 changes: 2 additions & 2 deletions src/distributions/exponential.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,10 @@ use distributions::{ziggurat, ziggurat_tables, Distribution};
///
/// # Example
/// ```rust
/// use rand::{weak_rng, Rng};
/// use rand::{NewRng, SmallRng, Rng};
/// use rand::distributions::Exp1;
///
/// let val: f64 = weak_rng().sample(Exp1);
/// let val: f64 = SmallRng::new().unwrap().sample(Exp1);
/// println!("{}", val);
/// ```
#[derive(Clone, Copy, Debug)]
Expand Down
4 changes: 2 additions & 2 deletions src/distributions/float.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,10 @@ macro_rules! float_impls {
///
/// # Example
/// ```rust
/// use rand::{weak_rng, Rng};
/// use rand::{NewRng, SmallRng, Rng};
/// use rand::distributions::Uniform;
///
/// let val: f32 = weak_rng().sample(Uniform);
/// let val: f32 = SmallRng::new().unwrap().sample(Uniform);
/// println!("f32 from (0,1): {}", val);
/// ```
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> $ty {
Expand Down
4 changes: 2 additions & 2 deletions src/distributions/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -163,10 +163,10 @@ impl<'a, T, D: Distribution<T>> Distribution<T> for &'a D {
///
/// # Example
/// ```rust
/// use rand::{weak_rng, Rng};
/// use rand::{NewRng, SmallRng, Rng};
/// use rand::distributions::Uniform;
///
/// let val: f32 = weak_rng().sample(Uniform);
/// let val: f32 = SmallRng::new().unwrap().sample(Uniform);
/// println!("f32 from [0,1): {}", val);
/// ```
///
Expand Down
4 changes: 2 additions & 2 deletions src/distributions/normal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,10 @@ use distributions::{ziggurat, ziggurat_tables, Distribution, Uniform};
///
/// # Example
/// ```rust
/// use rand::{weak_rng, Rng};
/// use rand::{NewRng, SmallRng, Rng};
/// use rand::distributions::StandardNormal;
///
/// let val: f64 = weak_rng().sample(StandardNormal);
/// let val: f64 = SmallRng::new().unwrap().sample(StandardNormal);
/// println!("{}", val);
/// ```
#[derive(Clone, Copy, Debug)]
Expand Down
96 changes: 83 additions & 13 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,11 @@

//! Utilities for random number generation
//!
//! The key functions are `random()` and `Rng::gen()`. These are polymorphic and
//! so can be used to generate any type supporting the [`Uniform`] distribution
//! (i.e. `T` where `Uniform`: `Distribution<T>`). Type inference
//! means that often a simple call to `rand::random()` or `rng.gen()` will
//! suffice, but sometimes an annotation is required, e.g.
//! `rand::random::<f64>()`.
//! The key function is `Rng::gen()`. It is polymorphic and so can be used to
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This probably wants a bit more re-writing, but this doesn't have to be done now.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I think we should advertise Rng::sample or Distribution::sample instead and possibly get rid of Rng::gen. But this is still under discussion in #293.

//! generate any type supporting the [`Uniform`] distribution (i.e. `T` where
//! `Uniform`: `Distribution<T>`). Type inference means that often a simple call
//! to `rng.gen()` will suffice, but sometimes an annotation is required, e.g.
//! `rng.gen::<f64>()`.
//!
//! See the `distributions` submodule for sampling random numbers from
//! distributions like normal and exponential.
Expand Down Expand Up @@ -88,11 +87,6 @@
//! }
//! ```
//!
//! ```rust
//! let tuple = rand::random::<(f64, char)>();
//! println!("{:?}", tuple)
//! ```
//!
//! ## Monte Carlo estimation of π
//!
//! For this example, imagine we have a square with sides of length 2 and a unit
Expand Down Expand Up @@ -289,7 +283,8 @@ pub use error::{ErrorKind, Error};

// convenience and derived rngs
#[cfg(feature="std")] pub use entropy_rng::EntropyRng;
#[cfg(feature="std")] pub use thread_rng::{ThreadRng, thread_rng, random};
#[cfg(feature="std")] pub use thread_rng::{ThreadRng, thread_rng};
#[cfg(feature="std")] #[allow(deprecated)] pub use thread_rng::random;

use distributions::{Distribution, Uniform, Range};
use distributions::range::SampleRange;
Expand Down Expand Up @@ -1091,6 +1086,81 @@ impl SeedableRng for StdRng {
}
}

/// An RNG recommended when small state, cheap initialization and good
/// performance are required. The PRNG algorithm in `SmallRng` is choosen to be
/// efficient on the current platform, **without consideration for cryptography
/// or security**. The size of its state is much smaller than for `StdRng`.
///
/// Reproducibility of output from this generator is however not required, thus
/// future library versions may use a different internal generator with
/// different output. Further, this generator may not be portable and can
/// produce different output depending on the architecture. If you require
/// reproducible output, use a named RNG, for example `XorShiftRng`.
///
/// The current algorithm used on all platforms is [Xorshift].
///
/// # Examples
///
/// Initializing `StdRng` with a random seed can be done using `NewRng`:
///
/// ```
/// use rand::{NewRng, SmallRng};
///
/// // Create small, cheap to initialize and fast RNG with a random seed.
/// // The randomness is supplied by the operating system.
/// let mut small_rng = SmallRng::new().unwrap();
/// ```
///
/// When initializing a lot of `SmallRng`, using `thread_rng` can be more
/// efficient:
///
/// ```
/// use rand::{SeedableRng, SmallRng, thread_rng};
///
/// // Create a big, expensive to initialize and slower, but unpredictable RNG.
/// // This is cached and done only once per thread.
/// let mut thread_rng = thread_rng();
/// // Create small, cheap to initialize and fast RNG with a random seed.
/// // This is very unlikely to fail.
/// let mut small_rng = SmallRng::from_rng(&mut thread_rng).unwrap();
/// ```
///
/// [Xorshift]: struct.XorShiftRng.html
#[derive(Clone, Debug)]
pub struct SmallRng(XorShiftRng);

impl RngCore for SmallRng {
fn next_u32(&mut self) -> u32 {
self.0.next_u32()
}

fn next_u64(&mut self) -> u64 {
self.0.next_u64()
}

fn fill_bytes(&mut self, dest: &mut [u8]) {
self.0.fill_bytes(dest);
}

fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> {
self.0.try_fill_bytes(dest)
}
}

impl SeedableRng for SmallRng {
type Seed = <XorShiftRng as SeedableRng>::Seed;

fn from_seed(seed: Self::Seed) -> Self {
SmallRng(XorShiftRng::from_seed(seed))
}

fn from_rng<R: Rng>(rng: &mut R) -> Result<Self, Error> {
XorShiftRng::from_rng(rng).map(|rng| SmallRng(rng))
}
}

/// DEPRECATED: use `SmallRng` instead.
///
/// Create a weak random number generator with a default algorithm and seed.
///
/// It returns the fastest `Rng` algorithm currently available in Rust without
Expand All @@ -1099,13 +1169,13 @@ impl SeedableRng for StdRng {
/// create the `Rng` yourself.
///
/// This will seed the generator with randomness from thread_rng.
#[deprecated(since="0.5.0", note="removed in favor of SmallRng")]
#[cfg(feature="std")]
pub fn weak_rng() -> XorShiftRng {
XorShiftRng::from_rng(&mut thread_rng()).unwrap_or_else(|err|
panic!("weak_rng failed: {:?}", err))
}


/// DEPRECATED: use `seq::sample_iter` instead.
///
/// Randomly sample up to `amount` elements from a finite iterator.
Expand Down
15 changes: 10 additions & 5 deletions src/thread_rng.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@
use std::cell::RefCell;
use std::rc::Rc;

use {RngCore, CryptoRng, StdRng, SeedableRng, EntropyRng};
use {Distribution, Uniform, Rng, Error};
use {RngCore, CryptoRng, StdRng, SeedableRng, EntropyRng, Error};
use {Distribution, Rng, Uniform};
use reseeding::ReseedingRng;

// Number of generated bytes after which to reseed `TreadRng`.
Expand Down Expand Up @@ -99,8 +99,10 @@ impl RngCore for ThreadRng {

impl CryptoRng for ThreadRng {}

/// DEPRECATED: use `thread_rng().gen()` instead.
///
/// Generates a random value using the thread-local random number generator.
///
///
/// This is simply a shortcut for `thread_rng().gen()`. See [`thread_rng`] for
/// documentation of the entropy source and [`Rand`] for documentation of
/// distributions and type-specific generation.
Expand Down Expand Up @@ -139,17 +141,18 @@ impl CryptoRng for ThreadRng {}
/// *x = rng.gen();
/// }
/// ```
///
///
/// [`thread_rng`]: fn.thread_rng.html
/// [`Rand`]: trait.Rand.html
#[deprecated(since="0.5.0", note="removed in favor of thread_rng().gen()")]
#[inline]
pub fn random<T>() -> T where Uniform: Distribution<T> {
thread_rng().gen()
}

#[cfg(test)]
mod test {
use super::*;
use Rng;

#[test]
#[cfg(feature="std")]
Expand All @@ -165,7 +168,9 @@ mod test {
}

#[test]
#[allow(deprecated)]
fn test_random() {
use super::random;
// not sure how to test this aside from just getting some values
let _n : usize = random();
let _f : f32 = random();
Expand Down