Skip to content

Commit

Permalink
Merge pull request #792 from dhardy/features
Browse files Browse the repository at this point in the history
Use ChaCha20 in StdRng and feature-gate SmallRng
  • Loading branch information
dhardy committed Jun 3, 2019
2 parents e108c47 + ff121cf commit 245fde0
Show file tree
Hide file tree
Showing 30 changed files with 268 additions and 241 deletions.
43 changes: 38 additions & 5 deletions Cargo.toml
Expand Up @@ -14,24 +14,30 @@ keywords = ["random", "rng"]
categories = ["algorithms", "no-std"]
build = "build.rs"
exclude = ["/utils/*", "/.travis.yml", "/appveyor.yml", ".gitignore"]
autobenches = true

[badges]
travis-ci = { repository = "rust-random/rand" }
appveyor = { repository = "rust-random/rand" }

[features]
# Meta-features:
default = ["std"] # without "std" rand uses libcore
nightly = ["simd_support"] # enables all features requiring nightly rust

# Optional dependencies:
std = ["rand_core/std", "alloc", "getrandom"]
alloc = ["rand_core/alloc"] # enables Vec and Box support (without std)
i128_support = [] # enables i128 and u128 support
simd_support = ["packed_simd"] # enables SIMD support
serde1 = ["rand_core/serde1", "rand_isaac/serde1", "rand_xorshift/serde1"] # enables serialization for PRNGs
# re-export optional WASM dependencies to avoid breakage:
wasm-bindgen = ["getrandom_package/wasm-bindgen"]
stdweb = ["getrandom_package/stdweb"]
getrandom = ["getrandom_package", "rand_core/getrandom"]

# Configuration:
simd_support = ["packed_simd"] # enables SIMD support
small_rng = ["rand_pcg"] # enables SmallRng

[workspace]
members = [
"rand_core",
Expand All @@ -49,8 +55,7 @@ members = [

[dependencies]
rand_core = { path = "rand_core", version = "0.4" }
rand_pcg = { path = "rand_pcg", version = "0.1" }
rand_hc = { path = "rand_hc", version = "0.1" }
rand_pcg = { path = "rand_pcg", version = "0.1", optional = true }
# Do not depend on 'getrandom_package' directly; use the 'getrandom' feature!
getrandom_package = { version = "0.1.1", package = "getrandom", optional = true }
log = { version = "0.4", optional = true }
Expand All @@ -66,17 +71,45 @@ features = ["into_bits"]
# Used for fork protection (reseeding.rs)
libc = { version = "0.2.22", default-features = false }

# Emscripten does not support 128-bit integers, which are used by ChaCha code.
# We work around this by using a different RNG.
[target.'cfg(not(target_os = "emscripten"))'.dependencies]
rand_chacha = { path = "rand_chacha", version = "0.2" }
[target.'cfg(target_os = "emscripten")'.dependencies]
rand_hc = { path = "rand_hc", version = "0.1" }

[dev-dependencies]
rand_pcg = { path = "rand_pcg", version = "0.1" }
# Only for benches:
rand_hc = { path = "rand_hc", version = "0.1" }
rand_xoshiro = { path = "rand_xoshiro", version = "0.2" }
rand_isaac = { path = "rand_isaac", version = "0.1" }
rand_chacha = { path = "rand_chacha", version = "0.2" }
rand_xorshift = { path = "rand_xorshift", version = "0.1" }
rand_distr = { path = "rand_distr", version = "0.1" }

[build-dependencies]
autocfg = "0.1"

[[bench]]
name = "distributions"
path = "benches/distributions.rs"
required-features = ["small_rng"]

[[bench]]
name = "generators"
path = "benches/generators.rs"
required-features = ["small_rng"]

[[bench]]
name = "misc"
path = "benches/misc.rs"
required-features = ["small_rng"]

[[bench]]
name = "seq"
path = "benches/seq.rs"
required-features = ["small_rng"]

[package.metadata.docs.rs]
all-features = true

Expand Down
63 changes: 28 additions & 35 deletions README.md
Expand Up @@ -84,41 +84,34 @@ pinned version of Rustc if you require compatibility with a specific version.

## Crate Features

Rand is built with the `std` and `getrandom` features enabled by default:

- `std` enables functionality dependent on the `std` lib and implies `alloc`
and `getrandom`
- `getrandom` is an optional crate, providing the code behind `rngs::OsRng`;
the continued existance of this feature is not guaranteed so users are
encouraged to specify `std` instead

The following optional features are available:

- `alloc` can be used instead of `std` to provide `Vec` and `Box`.
- `log` enables some logging via the `log` crate.
- `nightly` enables all unstable features (`simd_support`).
- `serde1` enables serialization for some types, via Serde version 1.
- `simd_support` enables uniform sampling of SIMD types (integers and floats).
- `stdweb` enables support for `OsRng` on `wasm32-unknown-unknown` via `stdweb`
combined with `cargo-web`.
- `wasm-bindgen` enables support for `OsRng` on `wasm32-unknown-unknown` via
[`wasm-bindgen`]

[`wasm-bindgen`]: https://github.com/rustwasm/wasm-bindgen

`no_std` mode is activated by setting `default-features = false`; this removes
functionality depending on `std`:

- `thread_rng()`, and `random()` are not available, as they require thread-local
storage and an entropy source.
- Since no external entropy is available, it is not possible to create
generators with fresh seeds using the `FromEntropy` trait (user must provide
a seed).
- Several non-linear distributions distributions are unavailable since `exp`
and `log` functions are not provided in `core`.
- Large parts of the `seq`-uence module are unavailable, unless the `alloc`
feature is used (several APIs and many implementations require `Vec`).

Rand is built with these features enabled by default:

- `std` enables functionality dependent on the `std` lib
- `alloc` (implied by `std`) enables functionality requiring an allocator
- `getrandom` (implied by `std`) is an optional dependency providing the code
behind `rngs::OsRng`

Optionally, the following dependencies can be enabled:

- `log` enables logging via the `log` crate
- `serde1` enables serialization for some types, via Serde version 1
- `stdweb` implies `getrandom/stdweb` to enable
`getrandom` support on `wasm32-unknown-unknown`
- `wasm-bindgen` implies `getrandom/wasm-bindgen` to enable
`getrandom` support on `wasm32-unknown-unknown`

Additionally, these features configure Rand:

- `small_rng` enables inclusion of the `SmallRng` PRNG
- `nightly` enables all experimental features
- `simd_support` (experimental) enables sampling of SIMD values
(uniformly random SIMD integers and floats)

Rand supports limited functionality in `no_std` mode (enabled via
`default-features = false`). In this case, `OsRng` and `from_entropy` are
unavailable (unless `getrandom` is enabled), large parts of `seq` are
unavailable (unless `alloc` is enabled), and `thread_rng` and `random` are
unavailable.

# License

Expand Down
51 changes: 20 additions & 31 deletions benches/generators.rs
Expand Up @@ -7,6 +7,7 @@
// except according to those terms.

#![feature(test)]
#![allow(non_snake_case)]

extern crate test;
extern crate rand;
Expand All @@ -27,8 +28,8 @@ use rand::prelude::*;
use rand::rngs::adapter::ReseedingRng;
use rand::rngs::OsRng;
use rand_isaac::{IsaacRng, Isaac64Rng};
use rand_chacha::{ChaCha8Rng, ChaCha12Rng, ChaCha20Rng};
use rand_hc::{Hc128Rng, Hc128Core};
use rand_chacha::{ChaCha20Core, ChaCha8Rng, ChaCha12Rng, ChaCha20Rng};
use rand_hc::{Hc128Rng};
use rand_pcg::{Lcg64Xsh32, Mcg128Xsl64};
use rand_xorshift::XorShiftRng;
use rand_xoshiro::{Xoshiro256StarStar, Xoshiro256Plus, Xoshiro128StarStar,
Expand Down Expand Up @@ -165,46 +166,34 @@ init_gen!(init_isaac, IsaacRng);
init_gen!(init_isaac64, Isaac64Rng);
init_gen!(init_chacha, ChaCha20Rng);

const RESEEDING_BYTES_LEN: usize = 1024 * 1024;
const RESEEDING_BENCH_N: u64 = 16;

const RESEEDING_THRESHOLD: u64 = 1024*1024*1024; // something high enough to get
// deterministic measurements

#[bench]
fn reseeding_hc128_bytes(b: &mut Bencher) {
let mut rng = ReseedingRng::new(Hc128Core::from_entropy(),
RESEEDING_THRESHOLD,
OsRng);
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) => {
macro_rules! reseeding_bytes {
($fnn:ident, $thresh:expr) => {
#[bench]
fn $fnn(b: &mut Bencher) {
let mut rng = ReseedingRng::new(Hc128Core::from_entropy(),
RESEEDING_THRESHOLD,
let mut rng = ReseedingRng::new(ChaCha20Core::from_entropy(),
$thresh * 1024,
OsRng);
let mut buf = [0u8; RESEEDING_BYTES_LEN];
b.iter(|| {
let mut accum: $ty = 0;
for _ in 0..RAND_BENCH_N {
accum = accum.wrapping_add(rng.gen::<$ty>());
for _ in 0..RESEEDING_BENCH_N {
rng.fill_bytes(&mut buf);
black_box(&buf);
}
accum
});
b.bytes = size_of::<$ty>() as u64 * RAND_BENCH_N;
b.bytes = RESEEDING_BYTES_LEN as u64 * RESEEDING_BENCH_N;
}
}
}

reseeding_uint!(reseeding_hc128_u32, u32);
reseeding_uint!(reseeding_hc128_u64, u64);
reseeding_bytes!(reseeding_chacha20_4k, 4);
reseeding_bytes!(reseeding_chacha20_16k, 16);
reseeding_bytes!(reseeding_chacha20_32k, 32);
reseeding_bytes!(reseeding_chacha20_64k, 64);
reseeding_bytes!(reseeding_chacha20_256k, 256);
reseeding_bytes!(reseeding_chacha20_1M, 1024);


macro_rules! threadrng_uint {
Expand Down
2 changes: 2 additions & 0 deletions rand_distr/Cargo.toml
Expand Up @@ -21,6 +21,8 @@ appveyor = { repository = "rust-random/rand" }
[dependencies]
rand = { path = "..", version = ">=0.5, <=0.7" }


[dev-dependencies]
rand_pcg = { version = "0.1", path = "../rand_pcg" }
# Histogram implementation for testing uniformity
average = "0.9.2"
20 changes: 7 additions & 13 deletions rand_distr/src/cauchy.rs
Expand Up @@ -87,30 +87,24 @@ mod test {
}

#[test]
fn test_cauchy_median() {
fn test_cauchy_averages() {
// NOTE: given that the variance and mean are undefined,
// this test does not have any rigorous statistical meaning.
let cauchy = Cauchy::new(10.0, 5.0).unwrap();
let mut rng = crate::test::rng(123);
let mut numbers: [f64; 1000] = [0.0; 1000];
let mut sum = 0.0;
for i in 0..1000 {
numbers[i] = cauchy.sample(&mut rng);
sum += numbers[i];
}
let median = median(&mut numbers);
println!("Cauchy median: {}", median);
assert!((median - 10.0).abs() < 0.5); // not 100% certain, but probable enough
}

#[test]
fn test_cauchy_mean() {
let cauchy = Cauchy::new(10.0, 5.0).unwrap();
let mut rng = crate::test::rng(123);
let mut sum = 0.0f64;
for _ in 0..1000 {
sum += cauchy.sample(&mut rng);
}
assert!((median - 10.0).abs() < 0.4); // not 100% certain, but probable enough
let mean = sum / 1000.0;
println!("Cauchy mean: {}", mean);
// for a Cauchy distribution the mean should not converge
assert!((mean - 10.0).abs() > 0.5); // not 100% certain, but probable enough
assert!((mean - 10.0).abs() > 0.4); // not 100% certain, but probable enough
}

#[test]
Expand Down
2 changes: 1 addition & 1 deletion rand_distr/src/exponential.rs
Expand Up @@ -33,7 +33,7 @@ use crate::utils::{ziggurat, Float};
/// use rand::prelude::*;
/// use rand_distr::Exp1;
///
/// let val: f64 = SmallRng::from_entropy().sample(Exp1);
/// let val: f64 = thread_rng().sample(Exp1);
/// println!("{}", val);
/// ```
#[derive(Clone, Copy, Debug)]
Expand Down
9 changes: 5 additions & 4 deletions rand_distr/src/lib.rs
Expand Up @@ -121,10 +121,11 @@ mod test {
// NOTE: Some distributions have tests checking only that samples can be
// generated. This is redundant with vector and correctness tests.

use rand::{RngCore, SeedableRng, rngs::StdRng};

/// Construct a deterministic RNG with the given seed
pub fn rng(seed: u64) -> impl RngCore {
StdRng::seed_from_u64(seed)
pub fn rng(seed: u64) -> impl rand::RngCore {
// For tests, we want a statistically good, fast, reproducible RNG.
// PCG32 will do fine, and will be easy to embed if we ever need to.
const INC: u64 = 11634580027462260723;
rand_pcg::Pcg32::new(seed, INC)
}
}
2 changes: 1 addition & 1 deletion rand_distr/src/normal.rs
Expand Up @@ -31,7 +31,7 @@ use crate::utils::{ziggurat, Float};
/// use rand::prelude::*;
/// use rand_distr::StandardNormal;
///
/// let val: f64 = SmallRng::from_entropy().sample(StandardNormal);
/// let val: f64 = thread_rng().sample(StandardNormal);
/// println!("{}", val);
/// ```
#[derive(Clone, Copy, Debug)]
Expand Down
2 changes: 1 addition & 1 deletion rand_distr/src/pareto.rs
Expand Up @@ -19,7 +19,7 @@ use crate::utils::Float;
/// use rand::prelude::*;
/// use rand_distr::Pareto;
///
/// let val: f64 = SmallRng::from_entropy().sample(Pareto::new(1., 2.).unwrap());
/// let val: f64 = thread_rng().sample(Pareto::new(1., 2.).unwrap());
/// println!("{}", val);
/// ```
#[derive(Clone, Copy, Debug)]
Expand Down
6 changes: 3 additions & 3 deletions rand_distr/src/pert.rs
Expand Up @@ -118,13 +118,13 @@ mod test {
}

#[test]
fn test_pert_vector() {
fn value_stability() {
let rng = crate::test::rng(860);
let distr = Pert::new(2., 10., 3.).unwrap(); // mean = 4, var = 12/7
let seq = distr.sample_iter(rng).take(5).collect::<Vec<f64>>();
println!("seq: {:?}", seq);
let expected = vec![3.945192480331639, 4.571769050527243,
7.419819712922435, 4.049743197259167, 5.825644880531534];
let expected = vec![4.631484136029422, 3.307201472321789,
3.29995019556348, 3.66835483991721, 3.514246139933899];
assert!(seq == expected);
}
}
6 changes: 3 additions & 3 deletions rand_distr/src/triangular.rs
Expand Up @@ -111,13 +111,13 @@ mod test {
}

#[test]
fn test_triangular_vector() {
fn value_stability() {
let rng = crate::test::rng(860);
let distr = Triangular::new(2., 10., 3.).unwrap();
let seq = distr.sample_iter(rng).take(5).collect::<Vec<f64>>();
println!("seq: {:?}", seq);
let expected = vec![4.941640229082449, 2.421447306833011,
4.5964271605527385, 2.789763631136542, 4.8014432067978445];
let expected = vec![5.74373257511361, 7.890059162791258,
4.7256280652553455, 2.9474808121184077, 3.058301946314053];
assert!(seq == expected);
}
}
6 changes: 3 additions & 3 deletions rand_distr/src/unit_ball.rs
Expand Up @@ -55,9 +55,9 @@ mod tests {
fn value_stability() {
let mut rng = crate::test::rng(2);
let expected = [
[-0.42140140089381806, 0.4245276448803281, -0.7109276652167549],
[0.6683277779168173, 0.12753134283863998, 0.6843687153674809],
[-0.80397712218568, -0.0015797354643116712, 0.1588400395442835],
[0.018035709265959987, -0.4348771383120438, -0.07982762085055706],
[0.10588569388223945, -0.4734350111375454, -0.7392104908825501],
[0.11060237642041049, -0.16065642822852677, -0.8444043930440075]
];
let samples: [[f64; 3]; 3] = [
UnitBall.sample(&mut rng),
Expand Down
6 changes: 3 additions & 3 deletions rand_distr/src/unit_circle.rs
Expand Up @@ -85,9 +85,9 @@ mod tests {
fn value_stability() {
let mut rng = crate::test::rng(2);
let expected = [
[-0.8032118336637037, 0.5956935036263119],
[-0.4742919588505423, -0.880367615130018],
[0.9297328981467168, 0.368234623716601],
[-0.9965658683520504, -0.08280380447614634],
[-0.9790853270389644, -0.20345004884984505],
[-0.8449189758898707, 0.5348943112253227],
];
let samples: [[f64; 2]; 3] = [
UnitCircle.sample(&mut rng),
Expand Down

0 comments on commit 245fde0

Please sign in to comment.