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

Use ChaCha20 in StdRng and feature-gate SmallRng #792

Merged
merged 7 commits into from Jun 3, 2019
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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