diff --git a/.github/workflows/benches.yml b/.github/workflows/benches.yml index f0112ec8..a862bd7e 100644 --- a/.github/workflows/benches.yml +++ b/.github/workflows/benches.yml @@ -23,4 +23,4 @@ jobs: - name: Clippy run: cargo clippy --all-targets -- -D warnings - name: Build - run: RUSTFLAGS=-Dwarnings cargo build --all-targets + run: RUSTFLAGS=-Dwarnings cargo test --benches diff --git a/CHANGELOG.md b/CHANGELOG.md index fca9d480..cd2eb46c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,7 +13,7 @@ You may also find the [Upgrade Guide](https://rust-random.github.io/book/update. - Add `IndexedRandom::choose_multiple_array`, `index::sample_array` (#1453, #1469) - Bump the MSRV to 1.61.0 - Rename `Rng::gen` to `Rng::random` to avoid conflict with the new `gen` keyword in Rust 2024 (#1435) -- Move all benchmarks to new `benches` crate (#1439) +- Move all benchmarks to new `benches` crate (#1439) and migrate to Criterion (#1490) - Annotate panicking methods with `#[track_caller]` (#1442, #1447) - Enable feature `small_rng` by default (#1455) - Allow `UniformFloat::new` samples and `UniformFloat::sample_single` to yield `high` (#1462) diff --git a/benches/Cargo.toml b/benches/Cargo.toml index 083512d0..a143bff3 100644 --- a/benches/Cargo.toml +++ b/benches/Cargo.toml @@ -15,26 +15,41 @@ criterion = "0.5" criterion-cycles-per-byte = "0.6" [[bench]] -name = "distr" -path = "src/distr.rs" +name = "array" harness = false [[bench]] -name = "uniform" -path = "src/uniform.rs" +name = "bool" +harness = false + +[[bench]] +name = "distr" +harness = false + +[[bench]] +name = "generators" harness = false [[bench]] name = "seq_choose" -path = "src/seq_choose.rs" harness = false [[bench]] name = "shuffle" -path = "src/shuffle.rs" +harness = false + +[[bench]] +name = "standard" +harness = false + +[[bench]] +name = "uniform" harness = false [[bench]] name = "uniform_float" -path = "src/uniform_float.rs" +harness = false + +[[bench]] +name = "weighted" harness = false diff --git a/benches/benches/array.rs b/benches/benches/array.rs new file mode 100644 index 00000000..c8d99dab --- /dev/null +++ b/benches/benches/array.rs @@ -0,0 +1,94 @@ +// Copyright 2018-2023 Developers of the Rand project. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Generating/filling arrays and iterators of output + +use criterion::{criterion_group, criterion_main, Criterion}; +use rand::distr::Standard; +use rand::prelude::*; +use rand_pcg::Pcg64Mcg; + +criterion_group!( + name = benches; + config = Criterion::default(); + targets = bench +); +criterion_main!(benches); + +pub fn bench(c: &mut Criterion) { + let mut g = c.benchmark_group("gen_1kb"); + g.throughput(criterion::Throughput::Bytes(1024)); + + g.bench_function("u16_iter_repeat", |b| { + use core::iter; + let mut rng = Pcg64Mcg::from_rng(&mut thread_rng()); + b.iter(|| { + let v: Vec = iter::repeat(()).map(|()| rng.random()).take(512).collect(); + v + }); + }); + + g.bench_function("u16_sample_iter", |b| { + let mut rng = Pcg64Mcg::from_rng(&mut thread_rng()); + b.iter(|| { + let v: Vec = Standard.sample_iter(&mut rng).take(512).collect(); + v + }); + }); + + g.bench_function("u16_gen_array", |b| { + let mut rng = Pcg64Mcg::from_rng(&mut thread_rng()); + b.iter(|| { + let v: [u16; 512] = rng.random(); + v + }); + }); + + g.bench_function("u16_fill", |b| { + let mut rng = Pcg64Mcg::from_rng(&mut thread_rng()); + let mut buf = [0u16; 512]; + b.iter(|| { + rng.fill(&mut buf[..]); + buf + }); + }); + + g.bench_function("u64_iter_repeat", |b| { + use core::iter; + let mut rng = Pcg64Mcg::from_rng(&mut thread_rng()); + b.iter(|| { + let v: Vec = iter::repeat(()).map(|()| rng.random()).take(128).collect(); + v + }); + }); + + g.bench_function("u64_sample_iter", |b| { + let mut rng = Pcg64Mcg::from_rng(&mut thread_rng()); + b.iter(|| { + let v: Vec = Standard.sample_iter(&mut rng).take(128).collect(); + v + }); + }); + + g.bench_function("u64_gen_array", |b| { + let mut rng = Pcg64Mcg::from_rng(&mut thread_rng()); + b.iter(|| { + let v: [u64; 128] = rng.random(); + v + }); + }); + + g.bench_function("u64_fill", |b| { + let mut rng = Pcg64Mcg::from_rng(&mut thread_rng()); + let mut buf = [0u64; 128]; + b.iter(|| { + rng.fill(&mut buf[..]); + buf + }); + }); +} diff --git a/benches/benches/base_distributions.rs b/benches/benches/base_distributions.rs deleted file mode 100644 index 17202a30..00000000 --- a/benches/benches/base_distributions.rs +++ /dev/null @@ -1,440 +0,0 @@ -// Copyright 2018 Developers of the Rand project. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -#![feature(custom_inner_attributes)] -#![feature(test)] - -// Rustfmt splits macro invocations to shorten lines; in this case longer-lines are more readable -#![rustfmt::skip] - -extern crate test; - -const RAND_BENCH_N: u64 = 1000; - -use rand::distr::{Alphanumeric, Open01, OpenClosed01, Standard, Uniform}; -use rand::distr::uniform::{UniformInt, UniformSampler}; -use core::mem::size_of; -use core::num::{NonZeroU128, NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU8}; -use core::time::Duration; -use test::{Bencher, black_box}; - -use rand::prelude::*; - -// At this time, distributions are optimised for 64-bit platforms. -use rand_pcg::Pcg64Mcg; - -macro_rules! distr_int { - ($fnn:ident, $ty:ty, $distr:expr) => { - #[bench] - fn $fnn(b: &mut Bencher) { - let mut rng = Pcg64Mcg::from_os_rng(); - let distr = $distr; - - b.iter(|| { - let mut accum = 0 as $ty; - for _ in 0..RAND_BENCH_N { - let x: $ty = distr.sample(&mut rng); - accum = accum.wrapping_add(x); - } - accum - }); - b.bytes = size_of::<$ty>() as u64 * RAND_BENCH_N; - } - }; -} - -macro_rules! distr_nz_int { - ($fnn:ident, $tynz:ty, $ty:ty, $distr:expr) => { - #[bench] - fn $fnn(b: &mut Bencher) { - let mut rng = Pcg64Mcg::from_os_rng(); - let distr = $distr; - - b.iter(|| { - let mut accum = 0 as $ty; - for _ in 0..RAND_BENCH_N { - let x: $tynz = distr.sample(&mut rng); - accum = accum.wrapping_add(x.get()); - } - accum - }); - b.bytes = size_of::<$ty>() as u64 * RAND_BENCH_N; - } - }; -} - -macro_rules! distr_float { - ($fnn:ident, $ty:ty, $distr:expr) => { - #[bench] - fn $fnn(b: &mut Bencher) { - let mut rng = Pcg64Mcg::from_os_rng(); - let distr = $distr; - - b.iter(|| { - let mut accum = 0.0; - for _ in 0..RAND_BENCH_N { - let x: $ty = distr.sample(&mut rng); - accum += x; - } - accum - }); - b.bytes = size_of::<$ty>() as u64 * RAND_BENCH_N; - } - }; -} - -macro_rules! distr_duration { - ($fnn:ident, $distr:expr) => { - #[bench] - fn $fnn(b: &mut Bencher) { - let mut rng = Pcg64Mcg::from_os_rng(); - let distr = $distr; - - b.iter(|| { - let mut accum = Duration::new(0, 0); - for _ in 0..RAND_BENCH_N { - let x: Duration = distr.sample(&mut rng); - accum = accum - .checked_add(x) - .unwrap_or(Duration::new(u64::MAX, 999_999_999)); - } - accum - }); - b.bytes = size_of::() as u64 * RAND_BENCH_N; - } - }; -} - -macro_rules! distr { - ($fnn:ident, $ty:ty, $distr:expr) => { - #[bench] - fn $fnn(b: &mut Bencher) { - let mut rng = Pcg64Mcg::from_os_rng(); - let distr = $distr; - - b.iter(|| { - let mut accum = 0u32; - for _ in 0..RAND_BENCH_N { - let x: $ty = distr.sample(&mut rng); - accum = accum.wrapping_add(x as u32); - } - accum - }); - b.bytes = size_of::<$ty>() as u64 * RAND_BENCH_N; - } - }; -} - -// uniform -distr_int!(distr_uniform_i8, i8, Uniform::new(20i8, 100).unwrap()); -distr_int!(distr_uniform_i16, i16, Uniform::new(-500i16, 2000).unwrap()); -distr_int!(distr_uniform_i32, i32, Uniform::new(-200_000_000i32, 800_000_000).unwrap()); -distr_int!(distr_uniform_i64, i64, Uniform::new(3i64, 123_456_789_123).unwrap()); -distr_int!(distr_uniform_i128, i128, Uniform::new(-123_456_789_123i128, 123_456_789_123_456_789).unwrap()); -distr_int!(distr_uniform_usize16, usize, Uniform::new(0usize, 0xb9d7).unwrap()); -distr_int!(distr_uniform_usize32, usize, Uniform::new(0usize, 0x548c0f43).unwrap()); -#[cfg(target_pointer_width = "64")] -distr_int!(distr_uniform_usize64, usize, Uniform::new(0usize, 0x3a42714f2bf927a8).unwrap()); -distr_int!(distr_uniform_isize, isize, Uniform::new(-1060478432isize, 1858574057).unwrap()); - -distr_float!(distr_uniform_f32, f32, Uniform::new(2.26f32, 2.319).unwrap()); -distr_float!(distr_uniform_f64, f64, Uniform::new(2.26f64, 2.319).unwrap()); - -const LARGE_SEC: u64 = u64::MAX / 1000; - -distr_duration!(distr_uniform_duration_largest, - Uniform::new_inclusive(Duration::new(0, 0), Duration::new(u64::MAX, 999_999_999)).unwrap() -); -distr_duration!(distr_uniform_duration_large, - Uniform::new(Duration::new(0, 0), Duration::new(LARGE_SEC, 1_000_000_000 / 2)).unwrap() -); -distr_duration!(distr_uniform_duration_one, - Uniform::new(Duration::new(0, 0), Duration::new(1, 0)).unwrap() -); -distr_duration!(distr_uniform_duration_variety, - Uniform::new(Duration::new(10000, 423423), Duration::new(200000, 6969954)).unwrap() -); -distr_duration!(distr_uniform_duration_edge, - Uniform::new_inclusive(Duration::new(LARGE_SEC, 999_999_999), Duration::new(LARGE_SEC + 1, 1)).unwrap() -); - -// standard -distr_int!(distr_standard_i8, i8, Standard); -distr_int!(distr_standard_i16, i16, Standard); -distr_int!(distr_standard_i32, i32, Standard); -distr_int!(distr_standard_i64, i64, Standard); -distr_int!(distr_standard_i128, i128, Standard); -distr_nz_int!(distr_standard_nz8, NonZeroU8, u8, Standard); -distr_nz_int!(distr_standard_nz16, NonZeroU16, u16, Standard); -distr_nz_int!(distr_standard_nz32, NonZeroU32, u32, Standard); -distr_nz_int!(distr_standard_nz64, NonZeroU64, u64, Standard); -distr_nz_int!(distr_standard_nz128, NonZeroU128, u128, Standard); - -distr!(distr_standard_bool, bool, Standard); -distr!(distr_standard_alphanumeric, u8, Alphanumeric); -distr!(distr_standard_codepoint, char, Standard); - -distr_float!(distr_standard_f32, f32, Standard); -distr_float!(distr_standard_f64, f64, Standard); -distr_float!(distr_open01_f32, f32, Open01); -distr_float!(distr_open01_f64, f64, Open01); -distr_float!(distr_openclosed01_f32, f32, OpenClosed01); -distr_float!(distr_openclosed01_f64, f64, OpenClosed01); - -// construct and sample from a range -macro_rules! gen_range_int { - ($fnn:ident, $ty:ident, $low:expr, $high:expr) => { - #[bench] - fn $fnn(b: &mut Bencher) { - let mut rng = Pcg64Mcg::from_os_rng(); - - b.iter(|| { - let mut high = $high; - let mut accum: $ty = 0; - for _ in 0..RAND_BENCH_N { - accum = accum.wrapping_add(rng.gen_range($low..high)); - // force recalculation of range each time - high = high.wrapping_add(1) & $ty::MAX; - } - accum - }); - b.bytes = size_of::<$ty>() as u64 * RAND_BENCH_N; - } - }; -} - -// Algorithms such as Fisher–Yates shuffle often require uniform values from an -// incrementing range 0..n. We use -1..n here to prevent wrapping in the test -// from generating a 0-sized range. -gen_range_int!(gen_range_i8_low, i8, -1i8, 0); -gen_range_int!(gen_range_i16_low, i16, -1i16, 0); -gen_range_int!(gen_range_i32_low, i32, -1i32, 0); -gen_range_int!(gen_range_i64_low, i64, -1i64, 0); -gen_range_int!(gen_range_i128_low, i128, -1i128, 0); - -// These were the initially tested ranges. They are likely to see fewer -// rejections than the low tests. -gen_range_int!(gen_range_i8_high, i8, -20i8, 100); -gen_range_int!(gen_range_i16_high, i16, -500i16, 2000); -gen_range_int!(gen_range_i32_high, i32, -200_000_000i32, 800_000_000); -gen_range_int!(gen_range_i64_high, i64, 3i64, 123_456_789_123); -gen_range_int!(gen_range_i128_high, i128, -12345678901234i128, 123_456_789_123_456_789); - -// construct and sample from a floating-point range -macro_rules! gen_range_float { - ($fnn:ident, $ty:ident, $low:expr, $high:expr) => { - #[bench] - fn $fnn(b: &mut Bencher) { - let mut rng = Pcg64Mcg::from_os_rng(); - - b.iter(|| { - let mut high = $high; - let mut low = $low; - let mut accum: $ty = 0.0; - for _ in 0..RAND_BENCH_N { - accum += rng.gen_range(low..high); - // force recalculation of range each time - low += 0.9; - high += 1.1; - } - accum - }); - b.bytes = size_of::<$ty>() as u64 * RAND_BENCH_N; - } - }; -} - -gen_range_float!(gen_range_f32, f32, -20000.0f32, 100000.0); -gen_range_float!(gen_range_f64, f64, 123.456f64, 7890.12); - - -// In src/distr/uniform.rs, we say: -// Implementation of [`uniform_single`] is optional, and is only useful when -// the implementation can be faster than `Self::new(low, high).sample(rng)`. - -// `UniformSampler::uniform_single` compromises on the rejection range to be -// faster. This benchmark demonstrates both the speed gain of doing this, and -// the worst case behavior. - -/// Sample random values from a pre-existing distribution. This uses the -/// half open `new` to be equivalent to the behavior of `uniform_single`. -macro_rules! uniform_sample { - ($fnn:ident, $type:ident, $low:expr, $high:expr, $count:expr) => { - #[bench] - fn $fnn(b: &mut Bencher) { - let mut rng = Pcg64Mcg::from_os_rng(); - let low = black_box($low); - let high = black_box($high); - b.iter(|| { - for _ in 0..10 { - let dist = UniformInt::<$type>::new(low, high).unwrap(); - for _ in 0..$count { - black_box(dist.sample(&mut rng)); - } - } - }); - } - }; -} - -macro_rules! uniform_inclusive { - ($fnn:ident, $type:ident, $low:expr, $high:expr, $count:expr) => { - #[bench] - fn $fnn(b: &mut Bencher) { - let mut rng = Pcg64Mcg::from_os_rng(); - let low = black_box($low); - let high = black_box($high); - b.iter(|| { - for _ in 0..10 { - let dist = UniformInt::<$type>::new_inclusive(low, high).unwrap(); - for _ in 0..$count { - black_box(dist.sample(&mut rng)); - } - } - }); - } - }; -} - -/// Use `uniform_single` to create a one-off random value -macro_rules! uniform_single { - ($fnn:ident, $type:ident, $low:expr, $high:expr, $count:expr) => { - #[bench] - fn $fnn(b: &mut Bencher) { - let mut rng = Pcg64Mcg::from_os_rng(); - let low = black_box($low); - let high = black_box($high); - b.iter(|| { - for _ in 0..(10 * $count) { - black_box(UniformInt::<$type>::sample_single(low, high, &mut rng).unwrap()); - } - }); - } - }; -} - - -// Benchmark: -// n: can use the full generated range -// (n-1): only the max value is rejected: expect this to be fast -// n/2+1: almost half of the values are rejected, and we can do no better -// n/2: approximation rejects half the values but powers of 2 could have no rejection -// n/2-1: only a few values are rejected: expect this to be fast -// 6: approximation rejects 25% of values but could be faster. However modulo by -// low numbers is typically more expensive - -// With the use of u32 as the minimum generated width, the worst-case u16 range -// (32769) will only reject 32769 / 4294967296 samples. -const HALF_16_BIT_UNSIGNED: u16 = 1 << 15; - -uniform_sample!(uniform_u16x1_allm1_new, u16, 0, u16::MAX, 1); -uniform_sample!(uniform_u16x1_halfp1_new, u16, 0, HALF_16_BIT_UNSIGNED + 1, 1); -uniform_sample!(uniform_u16x1_half_new, u16, 0, HALF_16_BIT_UNSIGNED, 1); -uniform_sample!(uniform_u16x1_halfm1_new, u16, 0, HALF_16_BIT_UNSIGNED - 1, 1); -uniform_sample!(uniform_u16x1_6_new, u16, 0, 6u16, 1); - -uniform_single!(uniform_u16x1_allm1_single, u16, 0, u16::MAX, 1); -uniform_single!(uniform_u16x1_halfp1_single, u16, 0, HALF_16_BIT_UNSIGNED + 1, 1); -uniform_single!(uniform_u16x1_half_single, u16, 0, HALF_16_BIT_UNSIGNED, 1); -uniform_single!(uniform_u16x1_halfm1_single, u16, 0, HALF_16_BIT_UNSIGNED - 1, 1); -uniform_single!(uniform_u16x1_6_single, u16, 0, 6u16, 1); - -uniform_inclusive!(uniform_u16x10_all_new_inclusive, u16, 0, u16::MAX, 10); -uniform_sample!(uniform_u16x10_allm1_new, u16, 0, u16::MAX, 10); -uniform_sample!(uniform_u16x10_halfp1_new, u16, 0, HALF_16_BIT_UNSIGNED + 1, 10); -uniform_sample!(uniform_u16x10_half_new, u16, 0, HALF_16_BIT_UNSIGNED, 10); -uniform_sample!(uniform_u16x10_halfm1_new, u16, 0, HALF_16_BIT_UNSIGNED - 1, 10); -uniform_sample!(uniform_u16x10_6_new, u16, 0, 6u16, 10); - -uniform_single!(uniform_u16x10_allm1_single, u16, 0, u16::MAX, 10); -uniform_single!(uniform_u16x10_halfp1_single, u16, 0, HALF_16_BIT_UNSIGNED + 1, 10); -uniform_single!(uniform_u16x10_half_single, u16, 0, HALF_16_BIT_UNSIGNED, 10); -uniform_single!(uniform_u16x10_halfm1_single, u16, 0, HALF_16_BIT_UNSIGNED - 1, 10); -uniform_single!(uniform_u16x10_6_single, u16, 0, 6u16, 10); - - -const HALF_32_BIT_UNSIGNED: u32 = 1 << 31; - -uniform_sample!(uniform_u32x1_allm1_new, u32, 0, u32::MAX, 1); -uniform_sample!(uniform_u32x1_halfp1_new, u32, 0, HALF_32_BIT_UNSIGNED + 1, 1); -uniform_sample!(uniform_u32x1_half_new, u32, 0, HALF_32_BIT_UNSIGNED, 1); -uniform_sample!(uniform_u32x1_halfm1_new, u32, 0, HALF_32_BIT_UNSIGNED - 1, 1); -uniform_sample!(uniform_u32x1_6_new, u32, 0, 6u32, 1); - -uniform_single!(uniform_u32x1_allm1_single, u32, 0, u32::MAX, 1); -uniform_single!(uniform_u32x1_halfp1_single, u32, 0, HALF_32_BIT_UNSIGNED + 1, 1); -uniform_single!(uniform_u32x1_half_single, u32, 0, HALF_32_BIT_UNSIGNED, 1); -uniform_single!(uniform_u32x1_halfm1_single, u32, 0, HALF_32_BIT_UNSIGNED - 1, 1); -uniform_single!(uniform_u32x1_6_single, u32, 0, 6u32, 1); - -uniform_inclusive!(uniform_u32x10_all_new_inclusive, u32, 0, u32::MAX, 10); -uniform_sample!(uniform_u32x10_allm1_new, u32, 0, u32::MAX, 10); -uniform_sample!(uniform_u32x10_halfp1_new, u32, 0, HALF_32_BIT_UNSIGNED + 1, 10); -uniform_sample!(uniform_u32x10_half_new, u32, 0, HALF_32_BIT_UNSIGNED, 10); -uniform_sample!(uniform_u32x10_halfm1_new, u32, 0, HALF_32_BIT_UNSIGNED - 1, 10); -uniform_sample!(uniform_u32x10_6_new, u32, 0, 6u32, 10); - -uniform_single!(uniform_u32x10_allm1_single, u32, 0, u32::MAX, 10); -uniform_single!(uniform_u32x10_halfp1_single, u32, 0, HALF_32_BIT_UNSIGNED + 1, 10); -uniform_single!(uniform_u32x10_half_single, u32, 0, HALF_32_BIT_UNSIGNED, 10); -uniform_single!(uniform_u32x10_halfm1_single, u32, 0, HALF_32_BIT_UNSIGNED - 1, 10); -uniform_single!(uniform_u32x10_6_single, u32, 0, 6u32, 10); - -const HALF_64_BIT_UNSIGNED: u64 = 1 << 63; - -uniform_sample!(uniform_u64x1_allm1_new, u64, 0, u64::MAX, 1); -uniform_sample!(uniform_u64x1_halfp1_new, u64, 0, HALF_64_BIT_UNSIGNED + 1, 1); -uniform_sample!(uniform_u64x1_half_new, u64, 0, HALF_64_BIT_UNSIGNED, 1); -uniform_sample!(uniform_u64x1_halfm1_new, u64, 0, HALF_64_BIT_UNSIGNED - 1, 1); -uniform_sample!(uniform_u64x1_6_new, u64, 0, 6u64, 1); - -uniform_single!(uniform_u64x1_allm1_single, u64, 0, u64::MAX, 1); -uniform_single!(uniform_u64x1_halfp1_single, u64, 0, HALF_64_BIT_UNSIGNED + 1, 1); -uniform_single!(uniform_u64x1_half_single, u64, 0, HALF_64_BIT_UNSIGNED, 1); -uniform_single!(uniform_u64x1_halfm1_single, u64, 0, HALF_64_BIT_UNSIGNED - 1, 1); -uniform_single!(uniform_u64x1_6_single, u64, 0, 6u64, 1); - -uniform_inclusive!(uniform_u64x10_all_new_inclusive, u64, 0, u64::MAX, 10); -uniform_sample!(uniform_u64x10_allm1_new, u64, 0, u64::MAX, 10); -uniform_sample!(uniform_u64x10_halfp1_new, u64, 0, HALF_64_BIT_UNSIGNED + 1, 10); -uniform_sample!(uniform_u64x10_half_new, u64, 0, HALF_64_BIT_UNSIGNED, 10); -uniform_sample!(uniform_u64x10_halfm1_new, u64, 0, HALF_64_BIT_UNSIGNED - 1, 10); -uniform_sample!(uniform_u64x10_6_new, u64, 0, 6u64, 10); - -uniform_single!(uniform_u64x10_allm1_single, u64, 0, u64::MAX, 10); -uniform_single!(uniform_u64x10_halfp1_single, u64, 0, HALF_64_BIT_UNSIGNED + 1, 10); -uniform_single!(uniform_u64x10_half_single, u64, 0, HALF_64_BIT_UNSIGNED, 10); -uniform_single!(uniform_u64x10_halfm1_single, u64, 0, HALF_64_BIT_UNSIGNED - 1, 10); -uniform_single!(uniform_u64x10_6_single, u64, 0, 6u64, 10); - -const HALF_128_BIT_UNSIGNED: u128 = 1 << 127; - -uniform_sample!(uniform_u128x1_allm1_new, u128, 0, u128::MAX, 1); -uniform_sample!(uniform_u128x1_halfp1_new, u128, 0, HALF_128_BIT_UNSIGNED + 1, 1); -uniform_sample!(uniform_u128x1_half_new, u128, 0, HALF_128_BIT_UNSIGNED, 1); -uniform_sample!(uniform_u128x1_halfm1_new, u128, 0, HALF_128_BIT_UNSIGNED - 1, 1); -uniform_sample!(uniform_u128x1_6_new, u128, 0, 6u128, 1); - -uniform_single!(uniform_u128x1_allm1_single, u128, 0, u128::MAX, 1); -uniform_single!(uniform_u128x1_halfp1_single, u128, 0, HALF_128_BIT_UNSIGNED + 1, 1); -uniform_single!(uniform_u128x1_half_single, u128, 0, HALF_128_BIT_UNSIGNED, 1); -uniform_single!(uniform_u128x1_halfm1_single, u128, 0, HALF_128_BIT_UNSIGNED - 1, 1); -uniform_single!(uniform_u128x1_6_single, u128, 0, 6u128, 1); - -uniform_inclusive!(uniform_u128x10_all_new_inclusive, u128, 0, u128::MAX, 10); -uniform_sample!(uniform_u128x10_allm1_new, u128, 0, u128::MAX, 10); -uniform_sample!(uniform_u128x10_halfp1_new, u128, 0, HALF_128_BIT_UNSIGNED + 1, 10); -uniform_sample!(uniform_u128x10_half_new, u128, 0, HALF_128_BIT_UNSIGNED, 10); -uniform_sample!(uniform_u128x10_halfm1_new, u128, 0, HALF_128_BIT_UNSIGNED - 1, 10); -uniform_sample!(uniform_u128x10_6_new, u128, 0, 6u128, 10); - -uniform_single!(uniform_u128x10_allm1_single, u128, 0, u128::MAX, 10); -uniform_single!(uniform_u128x10_halfp1_single, u128, 0, HALF_128_BIT_UNSIGNED + 1, 10); -uniform_single!(uniform_u128x10_half_single, u128, 0, HALF_128_BIT_UNSIGNED, 10); -uniform_single!(uniform_u128x10_halfm1_single, u128, 0, HALF_128_BIT_UNSIGNED - 1, 10); -uniform_single!(uniform_u128x10_6_single, u128, 0, 6u128, 10); diff --git a/benches/benches/bool.rs b/benches/benches/bool.rs new file mode 100644 index 00000000..e659ce5b --- /dev/null +++ b/benches/benches/bool.rs @@ -0,0 +1,69 @@ +// Copyright 2018 Developers of the Rand project. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Generating/filling arrays and iterators of output + +use criterion::{criterion_group, criterion_main, Criterion}; +use rand::distr::Bernoulli; +use rand::prelude::*; +use rand_pcg::Pcg32; + +criterion_group!( + name = benches; + config = Criterion::default(); + targets = bench +); +criterion_main!(benches); + +pub fn bench(c: &mut Criterion) { + let mut g = c.benchmark_group("gen_bool"); + g.sample_size(1000); + g.warm_up_time(core::time::Duration::from_millis(500)); + g.measurement_time(core::time::Duration::from_millis(1000)); + + g.bench_function("standard", |b| { + let mut rng = Pcg32::from_rng(&mut thread_rng()); + b.iter(|| rng.sample::(rand::distr::Standard)) + }); + + g.bench_function("const", |b| { + let mut rng = Pcg32::from_rng(&mut thread_rng()); + b.iter(|| rng.gen_bool(0.18)) + }); + + g.bench_function("var", |b| { + let mut rng = Pcg32::from_rng(&mut thread_rng()); + let p = rng.random(); + b.iter(|| rng.gen_bool(p)) + }); + + g.bench_function("ratio_const", |b| { + let mut rng = Pcg32::from_rng(&mut thread_rng()); + b.iter(|| rng.gen_ratio(2, 3)) + }); + + g.bench_function("ratio_var", |b| { + let mut rng = Pcg32::from_rng(&mut thread_rng()); + let d = rng.gen_range(1..=100); + let n = rng.gen_range(0..=d); + b.iter(|| rng.gen_ratio(n, d)); + }); + + g.bench_function("bernoulli_const", |b| { + let mut rng = Pcg32::from_rng(&mut thread_rng()); + let d = Bernoulli::new(0.18).unwrap(); + b.iter(|| rng.sample(d)) + }); + + g.bench_function("bernoulli_var", |b| { + let mut rng = Pcg32::from_rng(&mut thread_rng()); + let p = rng.random(); + let d = Bernoulli::new(p).unwrap(); + b.iter(|| rng.sample(d)) + }); +} diff --git a/benches/src/distr.rs b/benches/benches/distr.rs similarity index 99% rename from benches/src/distr.rs rename to benches/benches/distr.rs index 37b0c8d2..3097f3a9 100644 --- a/benches/src/distr.rs +++ b/benches/benches/distr.rs @@ -13,8 +13,7 @@ const RAND_BENCH_N: u64 = 1000; -use criterion::{criterion_group, criterion_main, Criterion, - Throughput}; +use criterion::{criterion_group, criterion_main, Criterion, Throughput}; use criterion_cycles_per_byte::CyclesPerByte; use core::mem::size_of; diff --git a/benches/benches/generators.rs b/benches/benches/generators.rs index 4019ec08..580d9897 100644 --- a/benches/benches/generators.rs +++ b/benches/benches/generators.rs @@ -6,139 +6,160 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![feature(test)] -#![allow(non_snake_case)] - -extern crate test; - -const RAND_BENCH_N: u64 = 1000; -const BYTES_LEN: usize = 1024; - -use core::mem::size_of; -use rand_chacha::rand_core::UnwrapErr; -use test::{black_box, Bencher}; - +use core::time::Duration; +use criterion::measurement::WallTime; +use criterion::{black_box, criterion_group, criterion_main, BenchmarkGroup, Criterion}; use rand::prelude::*; use rand::rngs::ReseedingRng; use rand::rngs::{mock::StepRng, OsRng}; +use rand_chacha::rand_core::UnwrapErr; use rand_chacha::{ChaCha12Rng, ChaCha20Core, ChaCha20Rng, ChaCha8Rng}; use rand_pcg::{Pcg32, Pcg64, Pcg64Dxsm, Pcg64Mcg}; -macro_rules! gen_bytes { - ($fnn:ident, $gen:expr) => { - #[bench] - fn $fnn(b: &mut Bencher) { - let mut rng = $gen; - let mut buf = [0u8; BYTES_LEN]; +criterion_group!( + name = benches; + config = Criterion::default(); + targets = gen_bytes, gen_u32, gen_u64, init_gen, reseeding_bytes +); +criterion_main!(benches); + +pub fn gen_bytes(c: &mut Criterion) { + let mut g = c.benchmark_group("gen_bytes"); + g.warm_up_time(Duration::from_millis(500)); + g.measurement_time(Duration::from_millis(1000)); + g.throughput(criterion::Throughput::Bytes(1024)); + + fn bench(g: &mut BenchmarkGroup, name: &str, mut rng: impl Rng) { + g.bench_function(name, |b| { + let mut buf = [0u8; 1024]; b.iter(|| { - for _ in 0..RAND_BENCH_N { - rng.fill_bytes(&mut buf); - black_box(buf); - } + rng.fill_bytes(&mut buf); + black_box(buf); }); - b.bytes = BYTES_LEN as u64 * RAND_BENCH_N; - } - }; + }); + } + + bench(&mut g, "step", StepRng::new(0, 1)); + bench(&mut g, "pcg32", Pcg32::from_os_rng()); + bench(&mut g, "pcg64", Pcg64::from_os_rng()); + bench(&mut g, "pcg64mcg", Pcg64Mcg::from_os_rng()); + bench(&mut g, "pcg64dxsm", Pcg64Dxsm::from_os_rng()); + bench(&mut g, "chacha8", ChaCha8Rng::from_os_rng()); + bench(&mut g, "chacha12", ChaCha12Rng::from_os_rng()); + bench(&mut g, "chacha20", ChaCha20Rng::from_os_rng()); + bench(&mut g, "std", StdRng::from_os_rng()); + bench(&mut g, "small", SmallRng::from_thread_rng()); + bench(&mut g, "os", UnwrapErr(OsRng)); + bench(&mut g, "thread", thread_rng()); + + g.finish() } -gen_bytes!(gen_bytes_step, StepRng::new(0, 1)); -gen_bytes!(gen_bytes_pcg32, Pcg32::from_os_rng()); -gen_bytes!(gen_bytes_pcg64, Pcg64::from_os_rng()); -gen_bytes!(gen_bytes_pcg64mcg, Pcg64Mcg::from_os_rng()); -gen_bytes!(gen_bytes_pcg64dxsm, Pcg64Dxsm::from_os_rng()); -gen_bytes!(gen_bytes_chacha8, ChaCha8Rng::from_os_rng()); -gen_bytes!(gen_bytes_chacha12, ChaCha12Rng::from_os_rng()); -gen_bytes!(gen_bytes_chacha20, ChaCha20Rng::from_os_rng()); -gen_bytes!(gen_bytes_std, StdRng::from_os_rng()); -gen_bytes!(gen_bytes_small, SmallRng::from_thread_rng()); -gen_bytes!(gen_bytes_os, UnwrapErr(OsRng)); -gen_bytes!(gen_bytes_thread, thread_rng()); +pub fn gen_u32(c: &mut Criterion) { + let mut g = c.benchmark_group("gen_u32"); + g.sample_size(1000); + g.warm_up_time(Duration::from_millis(500)); + g.measurement_time(Duration::from_millis(1000)); + g.throughput(criterion::Throughput::Bytes(4)); -macro_rules! gen_uint { - ($fnn:ident, $ty:ty, $gen:expr) => { - #[bench] - fn $fnn(b: &mut Bencher) { - let mut rng = $gen; - b.iter(|| { - let mut accum: $ty = 0; - for _ in 0..RAND_BENCH_N { - accum = accum.wrapping_add(rng.random::<$ty>()); - } - accum - }); - b.bytes = size_of::<$ty>() as u64 * RAND_BENCH_N; - } - }; + fn bench(g: &mut BenchmarkGroup, name: &str, mut rng: impl Rng) { + g.bench_function(name, |b| { + b.iter(|| rng.random::()); + }); + } + + bench(&mut g, "step", StepRng::new(0, 1)); + bench(&mut g, "pcg32", Pcg32::from_os_rng()); + bench(&mut g, "pcg64", Pcg64::from_os_rng()); + bench(&mut g, "pcg64mcg", Pcg64Mcg::from_os_rng()); + bench(&mut g, "pcg64dxsm", Pcg64Dxsm::from_os_rng()); + bench(&mut g, "chacha8", ChaCha8Rng::from_os_rng()); + bench(&mut g, "chacha12", ChaCha12Rng::from_os_rng()); + bench(&mut g, "chacha20", ChaCha20Rng::from_os_rng()); + bench(&mut g, "std", StdRng::from_os_rng()); + bench(&mut g, "small", SmallRng::from_thread_rng()); + bench(&mut g, "os", UnwrapErr(OsRng)); + bench(&mut g, "thread", thread_rng()); + + g.finish() } -gen_uint!(gen_u32_step, u32, StepRng::new(0, 1)); -gen_uint!(gen_u32_pcg32, u32, Pcg32::from_os_rng()); -gen_uint!(gen_u32_pcg64, u32, Pcg64::from_os_rng()); -gen_uint!(gen_u32_pcg64mcg, u32, Pcg64Mcg::from_os_rng()); -gen_uint!(gen_u32_pcg64dxsm, u32, Pcg64Dxsm::from_os_rng()); -gen_uint!(gen_u32_chacha8, u32, ChaCha8Rng::from_os_rng()); -gen_uint!(gen_u32_chacha12, u32, ChaCha12Rng::from_os_rng()); -gen_uint!(gen_u32_chacha20, u32, ChaCha20Rng::from_os_rng()); -gen_uint!(gen_u32_std, u32, StdRng::from_os_rng()); -gen_uint!(gen_u32_small, u32, SmallRng::from_thread_rng()); -gen_uint!(gen_u32_os, u32, UnwrapErr(OsRng)); -gen_uint!(gen_u32_thread, u32, thread_rng()); +pub fn gen_u64(c: &mut Criterion) { + let mut g = c.benchmark_group("gen_u64"); + g.sample_size(1000); + g.warm_up_time(Duration::from_millis(500)); + g.measurement_time(Duration::from_millis(1000)); + g.throughput(criterion::Throughput::Bytes(8)); -gen_uint!(gen_u64_step, u64, StepRng::new(0, 1)); -gen_uint!(gen_u64_pcg32, u64, Pcg32::from_os_rng()); -gen_uint!(gen_u64_pcg64, u64, Pcg64::from_os_rng()); -gen_uint!(gen_u64_pcg64mcg, u64, Pcg64Mcg::from_os_rng()); -gen_uint!(gen_u64_pcg64dxsm, u64, Pcg64Dxsm::from_os_rng()); -gen_uint!(gen_u64_chacha8, u64, ChaCha8Rng::from_os_rng()); -gen_uint!(gen_u64_chacha12, u64, ChaCha12Rng::from_os_rng()); -gen_uint!(gen_u64_chacha20, u64, ChaCha20Rng::from_os_rng()); -gen_uint!(gen_u64_std, u64, StdRng::from_os_rng()); -gen_uint!(gen_u64_small, u64, SmallRng::from_thread_rng()); -gen_uint!(gen_u64_os, u64, UnwrapErr(OsRng)); -gen_uint!(gen_u64_thread, u64, thread_rng()); + fn bench(g: &mut BenchmarkGroup, name: &str, mut rng: impl Rng) { + g.bench_function(name, |b| { + b.iter(|| rng.random::()); + }); + } -macro_rules! init_gen { - ($fnn:ident, $gen:ident) => { - #[bench] - fn $fnn(b: &mut Bencher) { + bench(&mut g, "step", StepRng::new(0, 1)); + bench(&mut g, "pcg32", Pcg32::from_os_rng()); + bench(&mut g, "pcg64", Pcg64::from_os_rng()); + bench(&mut g, "pcg64mcg", Pcg64Mcg::from_os_rng()); + bench(&mut g, "pcg64dxsm", Pcg64Dxsm::from_os_rng()); + bench(&mut g, "chacha8", ChaCha8Rng::from_os_rng()); + bench(&mut g, "chacha12", ChaCha12Rng::from_os_rng()); + bench(&mut g, "chacha20", ChaCha20Rng::from_os_rng()); + bench(&mut g, "std", StdRng::from_os_rng()); + bench(&mut g, "small", SmallRng::from_thread_rng()); + bench(&mut g, "os", UnwrapErr(OsRng)); + bench(&mut g, "thread", thread_rng()); + + g.finish() +} + +pub fn init_gen(c: &mut Criterion) { + let mut g = c.benchmark_group("init_gen"); + g.warm_up_time(Duration::from_millis(500)); + g.measurement_time(Duration::from_millis(1000)); + + fn bench(g: &mut BenchmarkGroup, name: &str) { + g.bench_function(name, |b| { let mut rng = Pcg32::from_os_rng(); - b.iter(|| { - let r2 = $gen::from_rng(&mut rng); - r2 - }); - } - }; + b.iter(|| R::from_rng(&mut rng)); + }); + } + + bench::(&mut g, "pcg32"); + bench::(&mut g, "pcg64"); + bench::(&mut g, "pcg64mcg"); + bench::(&mut g, "pcg64dxsm"); + bench::(&mut g, "chacha8"); + bench::(&mut g, "chacha12"); + bench::(&mut g, "chacha20"); + bench::(&mut g, "std"); + + g.finish() } -init_gen!(init_pcg32, Pcg32); -init_gen!(init_pcg64, Pcg64); -init_gen!(init_pcg64mcg, Pcg64Mcg); -init_gen!(init_pcg64dxsm, Pcg64Dxsm); -init_gen!(init_chacha, ChaCha20Rng); +pub fn reseeding_bytes(c: &mut Criterion) { + let mut g = c.benchmark_group("reseeding_bytes"); + g.warm_up_time(Duration::from_millis(500)); + g.throughput(criterion::Throughput::Bytes(1024 * 1024)); -const RESEEDING_BYTES_LEN: usize = 1024 * 1024; -const RESEEDING_BENCH_N: u64 = 16; - -macro_rules! reseeding_bytes { - ($fnn:ident, $thresh:expr) => { - #[bench] - fn $fnn(b: &mut Bencher) { - let mut rng = ReseedingRng::new(ChaCha20Core::from_os_rng(), $thresh * 1024, OsRng); - let mut buf = [0u8; RESEEDING_BYTES_LEN]; + fn bench(g: &mut BenchmarkGroup, thresh: u64) { + let name = format!("chacha20_{}k", thresh); + g.bench_function(name.as_str(), |b| { + let mut rng = ReseedingRng::new(ChaCha20Core::from_os_rng(), thresh * 1024, OsRng); + let mut buf = [0u8; 1024 * 1024]; b.iter(|| { - for _ in 0..RESEEDING_BENCH_N { - rng.fill_bytes(&mut buf); - black_box(&buf); - } + rng.fill_bytes(&mut buf); + black_box(&buf); }); - b.bytes = RESEEDING_BYTES_LEN as u64 * RESEEDING_BENCH_N; - } - }; -} + }); + } -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); + bench(&mut g, 4); + bench(&mut g, 16); + bench(&mut g, 32); + bench(&mut g, 64); + bench(&mut g, 256); + bench(&mut g, 1024); + + g.finish() +} diff --git a/benches/benches/misc.rs b/benches/benches/misc.rs deleted file mode 100644 index 8a3b4767..00000000 --- a/benches/benches/misc.rs +++ /dev/null @@ -1,183 +0,0 @@ -// Copyright 2018 Developers of the Rand project. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -#![feature(test)] - -extern crate test; - -const RAND_BENCH_N: u64 = 1000; - -use test::Bencher; - -use rand::distr::{Bernoulli, Distribution, Standard}; -use rand::prelude::*; -use rand_pcg::{Pcg32, Pcg64Mcg}; - -#[bench] -fn misc_gen_bool_const(b: &mut Bencher) { - let mut rng = Pcg32::from_rng(&mut thread_rng()); - b.iter(|| { - let mut accum = true; - for _ in 0..RAND_BENCH_N { - accum ^= rng.gen_bool(0.18); - } - accum - }) -} - -#[bench] -fn misc_gen_bool_var(b: &mut Bencher) { - let mut rng = Pcg32::from_rng(&mut thread_rng()); - b.iter(|| { - let mut accum = true; - let mut p = 0.18; - for _ in 0..RAND_BENCH_N { - accum ^= rng.gen_bool(p); - p += 0.0001; - } - accum - }) -} - -#[bench] -fn misc_gen_ratio_const(b: &mut Bencher) { - let mut rng = Pcg32::from_rng(&mut thread_rng()); - b.iter(|| { - let mut accum = true; - for _ in 0..RAND_BENCH_N { - accum ^= rng.gen_ratio(2, 3); - } - accum - }) -} - -#[bench] -fn misc_gen_ratio_var(b: &mut Bencher) { - let mut rng = Pcg32::from_rng(&mut thread_rng()); - b.iter(|| { - let mut accum = true; - for i in 2..(RAND_BENCH_N as u32 + 2) { - accum ^= rng.gen_ratio(i, i + 1); - } - accum - }) -} - -#[bench] -fn misc_bernoulli_const(b: &mut Bencher) { - let mut rng = Pcg32::from_rng(&mut thread_rng()); - b.iter(|| { - let d = Bernoulli::new(0.18).unwrap(); - let mut accum = true; - for _ in 0..RAND_BENCH_N { - accum ^= rng.sample(d); - } - accum - }) -} - -#[bench] -fn misc_bernoulli_var(b: &mut Bencher) { - let mut rng = Pcg32::from_rng(&mut thread_rng()); - b.iter(|| { - let mut accum = true; - let mut p = 0.18; - for _ in 0..RAND_BENCH_N { - let d = Bernoulli::new(p).unwrap(); - accum ^= rng.sample(d); - p += 0.0001; - } - accum - }) -} - -#[bench] -fn gen_1kb_u16_iter_repeat(b: &mut Bencher) { - use core::iter; - let mut rng = Pcg64Mcg::from_rng(&mut thread_rng()); - b.iter(|| { - let v: Vec = iter::repeat(()).map(|()| rng.random()).take(512).collect(); - v - }); - b.bytes = 1024; -} - -#[bench] -fn gen_1kb_u16_sample_iter(b: &mut Bencher) { - let mut rng = Pcg64Mcg::from_rng(&mut thread_rng()); - b.iter(|| { - let v: Vec = Standard.sample_iter(&mut rng).take(512).collect(); - v - }); - b.bytes = 1024; -} - -#[bench] -fn gen_1kb_u16_gen_array(b: &mut Bencher) { - let mut rng = Pcg64Mcg::from_rng(&mut thread_rng()); - b.iter(|| { - // max supported array length is 32! - let v: [[u16; 32]; 16] = rng.random(); - v - }); - b.bytes = 1024; -} - -#[bench] -fn gen_1kb_u16_fill(b: &mut Bencher) { - let mut rng = Pcg64Mcg::from_rng(&mut thread_rng()); - let mut buf = [0u16; 512]; - b.iter(|| { - rng.fill(&mut buf[..]); - buf - }); - b.bytes = 1024; -} - -#[bench] -fn gen_1kb_u64_iter_repeat(b: &mut Bencher) { - use core::iter; - let mut rng = Pcg64Mcg::from_rng(&mut thread_rng()); - b.iter(|| { - let v: Vec = iter::repeat(()).map(|()| rng.random()).take(128).collect(); - v - }); - b.bytes = 1024; -} - -#[bench] -fn gen_1kb_u64_sample_iter(b: &mut Bencher) { - let mut rng = Pcg64Mcg::from_rng(&mut thread_rng()); - b.iter(|| { - let v: Vec = Standard.sample_iter(&mut rng).take(128).collect(); - v - }); - b.bytes = 1024; -} - -#[bench] -fn gen_1kb_u64_gen_array(b: &mut Bencher) { - let mut rng = Pcg64Mcg::from_rng(&mut thread_rng()); - b.iter(|| { - // max supported array length is 32! - let v: [[u64; 32]; 4] = rng.random(); - v - }); - b.bytes = 1024; -} - -#[bench] -fn gen_1kb_u64_fill(b: &mut Bencher) { - let mut rng = Pcg64Mcg::from_rng(&mut thread_rng()); - let mut buf = [0u64; 128]; - b.iter(|| { - rng.fill(&mut buf[..]); - buf - }); - b.bytes = 1024; -} diff --git a/benches/benches/seq.rs b/benches/benches/seq.rs deleted file mode 100644 index 8b4b774b..00000000 --- a/benches/benches/seq.rs +++ /dev/null @@ -1,130 +0,0 @@ -// Copyright 2018 Developers of the Rand project. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -#![feature(test)] -#![allow(non_snake_case)] - -extern crate test; - -use test::Bencher; - -use core::mem::size_of; -use rand::prelude::*; -use rand::seq::*; - -// We force use of 32-bit RNG since seq code is optimised for use with 32-bit -// generators on all platforms. -use rand_pcg::Pcg32 as SmallRng; - -const RAND_BENCH_N: u64 = 1000; - -#[bench] -fn seq_shuffle_100(b: &mut Bencher) { - let mut rng = SmallRng::from_rng(thread_rng()); - let x: &mut [usize] = &mut [1; 100]; - b.iter(|| { - x.shuffle(&mut rng); - x[0] - }) -} - -#[bench] -fn seq_slice_choose_1_of_1000(b: &mut Bencher) { - let mut rng = SmallRng::from_rng(thread_rng()); - let x: &mut [usize] = &mut [1; 1000]; - for (i, r) in x.iter_mut().enumerate() { - *r = i; - } - b.iter(|| { - let mut s = 0; - for _ in 0..RAND_BENCH_N { - s += x.choose(&mut rng).unwrap(); - } - s - }); - b.bytes = size_of::() as u64 * RAND_BENCH_N; -} - -macro_rules! seq_slice_choose_multiple { - ($name:ident, $amount:expr, $length:expr) => { - #[bench] - fn $name(b: &mut Bencher) { - let mut rng = SmallRng::from_rng(thread_rng()); - let x: &[i32] = &[$amount; $length]; - let mut result = [0i32; $amount]; - b.iter(|| { - // Collect full result to prevent unwanted shortcuts getting - // first element (in case sample_indices returns an iterator). - for (slot, sample) in result.iter_mut().zip(x.choose_multiple(&mut rng, $amount)) { - *slot = *sample; - } - result[$amount - 1] - }) - } - }; -} - -seq_slice_choose_multiple!(seq_slice_choose_multiple_1_of_1000, 1, 1000); -seq_slice_choose_multiple!(seq_slice_choose_multiple_950_of_1000, 950, 1000); -seq_slice_choose_multiple!(seq_slice_choose_multiple_10_of_100, 10, 100); -seq_slice_choose_multiple!(seq_slice_choose_multiple_90_of_100, 90, 100); - -#[bench] -fn seq_iter_choose_multiple_10_of_100(b: &mut Bencher) { - let mut rng = SmallRng::from_rng(thread_rng()); - let x: &[usize] = &[1; 100]; - b.iter(|| x.iter().cloned().choose_multiple(&mut rng, 10)) -} - -#[bench] -fn seq_iter_choose_multiple_fill_10_of_100(b: &mut Bencher) { - let mut rng = SmallRng::from_rng(thread_rng()); - let x: &[usize] = &[1; 100]; - let mut buf = [0; 10]; - b.iter(|| x.iter().cloned().choose_multiple_fill(&mut rng, &mut buf)) -} - -macro_rules! sample_indices { - ($name:ident, $fn:ident, $amount:expr, $length:expr) => { - #[bench] - fn $name(b: &mut Bencher) { - let mut rng = SmallRng::from_rng(thread_rng()); - b.iter(|| index::$fn(&mut rng, $length, $amount)) - } - }; -} - -sample_indices!(misc_sample_indices_1_of_1k, sample, 1, 1000); -sample_indices!(misc_sample_indices_10_of_1k, sample, 10, 1000); -sample_indices!(misc_sample_indices_100_of_1k, sample, 100, 1000); -sample_indices!(misc_sample_indices_100_of_1M, sample, 100, 1_000_000); -sample_indices!(misc_sample_indices_100_of_1G, sample, 100, 1_000_000_000); -sample_indices!(misc_sample_indices_200_of_1G, sample, 200, 1_000_000_000); -sample_indices!(misc_sample_indices_400_of_1G, sample, 400, 1_000_000_000); -sample_indices!(misc_sample_indices_600_of_1G, sample, 600, 1_000_000_000); - -macro_rules! sample_indices_rand_weights { - ($name:ident, $amount:expr, $length:expr) => { - #[bench] - fn $name(b: &mut Bencher) { - let mut rng = SmallRng::from_rng(thread_rng()); - b.iter(|| { - index::sample_weighted(&mut rng, $length, |idx| (1 + (idx % 100)) as u32, $amount) - }) - } - }; -} - -sample_indices_rand_weights!(misc_sample_weighted_indices_1_of_1k, 1, 1000); -sample_indices_rand_weights!(misc_sample_weighted_indices_10_of_1k, 10, 1000); -sample_indices_rand_weights!(misc_sample_weighted_indices_100_of_1k, 100, 1000); -sample_indices_rand_weights!(misc_sample_weighted_indices_100_of_1M, 100, 1_000_000); -sample_indices_rand_weights!(misc_sample_weighted_indices_200_of_1M, 200, 1_000_000); -sample_indices_rand_weights!(misc_sample_weighted_indices_400_of_1M, 400, 1_000_000); -sample_indices_rand_weights!(misc_sample_weighted_indices_600_of_1M, 600, 1_000_000); -sample_indices_rand_weights!(misc_sample_weighted_indices_1k_of_1M, 1000, 1_000_000); diff --git a/benches/benches/seq_choose.rs b/benches/benches/seq_choose.rs new file mode 100644 index 00000000..f418f9cc --- /dev/null +++ b/benches/benches/seq_choose.rs @@ -0,0 +1,155 @@ +// Copyright 2018-2023 Developers of the Rand project. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use criterion::{black_box, criterion_group, criterion_main, Criterion}; +use rand::prelude::*; +use rand::SeedableRng; +use rand_pcg::Pcg32; + +criterion_group!( + name = benches; + config = Criterion::default(); + targets = bench +); +criterion_main!(benches); + +pub fn bench(c: &mut Criterion) { + c.bench_function("seq_slice_choose_1_of_1000", |b| { + let mut rng = Pcg32::from_rng(thread_rng()); + let mut buf = [0i32; 100]; + rng.fill(&mut buf); + let x = black_box(&mut buf); + + b.iter(|| x.choose(&mut rng).unwrap()); + }); + + let lens = [(1, 1000), (950, 1000), (10, 100), (90, 100)]; + for (amount, len) in lens { + let name = format!("seq_slice_choose_multiple_{}_of_{}", amount, len); + c.bench_function(name.as_str(), |b| { + let mut rng = Pcg32::from_rng(thread_rng()); + let mut buf = [0i32; 1000]; + rng.fill(&mut buf); + let x = black_box(&buf[..len]); + + let mut results_buf = [0i32; 950]; + let y = black_box(&mut results_buf[..amount]); + let amount = black_box(amount); + + b.iter(|| { + // Collect full result to prevent unwanted shortcuts getting + // first element (in case sample_indices returns an iterator). + for (slot, sample) in y.iter_mut().zip(x.choose_multiple(&mut rng, amount)) { + *slot = *sample; + } + y[amount - 1] + }) + }); + } + + c.bench_function("seq_iter_choose_multiple_10_of_100", |b| { + let mut rng = Pcg32::from_rng(thread_rng()); + let mut buf = [0i32; 100]; + rng.fill(&mut buf); + let x = black_box(&buf); + b.iter(|| x.iter().cloned().choose_multiple(&mut rng, 10)) + }); + + c.bench_function("seq_iter_choose_multiple_fill_10_of_100", |b| { + let mut rng = Pcg32::from_rng(thread_rng()); + let mut buf = [0i32; 100]; + rng.fill(&mut buf); + let x = black_box(&buf); + let mut buf = [0; 10]; + b.iter(|| x.iter().cloned().choose_multiple_fill(&mut rng, &mut buf)) + }); + + bench_rng::(c, "ChaCha20"); + bench_rng::(c, "Pcg32"); + bench_rng::(c, "Pcg64"); +} + +fn bench_rng(c: &mut Criterion, rng_name: &'static str) { + for length in [1, 2, 3, 10, 100, 1000].map(black_box) { + let name = format!("choose_size-hinted_from_{length}_{rng_name}"); + c.bench_function(name.as_str(), |b| { + let mut rng = Rng::seed_from_u64(123); + b.iter(|| choose_size_hinted(length, &mut rng)) + }); + + let name = format!("choose_stable_from_{length}_{rng_name}"); + c.bench_function(name.as_str(), |b| { + let mut rng = Rng::seed_from_u64(123); + b.iter(|| choose_stable(length, &mut rng)) + }); + + let name = format!("choose_unhinted_from_{length}_{rng_name}"); + c.bench_function(name.as_str(), |b| { + let mut rng = Rng::seed_from_u64(123); + b.iter(|| choose_unhinted(length, &mut rng)) + }); + + let name = format!("choose_windowed_from_{length}_{rng_name}"); + c.bench_function(name.as_str(), |b| { + let mut rng = Rng::seed_from_u64(123); + b.iter(|| choose_windowed(length, 7, &mut rng)) + }); + } +} + +fn choose_size_hinted(max: usize, rng: &mut R) -> Option { + let iterator = 0..max; + iterator.choose(rng) +} + +fn choose_stable(max: usize, rng: &mut R) -> Option { + let iterator = 0..max; + iterator.choose_stable(rng) +} + +fn choose_unhinted(max: usize, rng: &mut R) -> Option { + let iterator = UnhintedIterator { iter: (0..max) }; + iterator.choose(rng) +} + +fn choose_windowed(max: usize, window_size: usize, rng: &mut R) -> Option { + let iterator = WindowHintedIterator { + iter: (0..max), + window_size, + }; + iterator.choose(rng) +} + +#[derive(Clone)] +struct UnhintedIterator { + iter: I, +} +impl Iterator for UnhintedIterator { + type Item = I::Item; + + fn next(&mut self) -> Option { + self.iter.next() + } +} + +#[derive(Clone)] +struct WindowHintedIterator { + iter: I, + window_size: usize, +} +impl Iterator for WindowHintedIterator { + type Item = I::Item; + + fn next(&mut self) -> Option { + self.iter.next() + } + + fn size_hint(&self) -> (usize, Option) { + (core::cmp::min(self.iter.len(), self.window_size), None) + } +} diff --git a/benches/src/shuffle.rs b/benches/benches/shuffle.rs similarity index 60% rename from benches/src/shuffle.rs rename to benches/benches/shuffle.rs index 4d6e31fa..106d12e0 100644 --- a/benches/src/shuffle.rs +++ b/benches/benches/shuffle.rs @@ -5,18 +5,31 @@ // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. + use criterion::{black_box, criterion_group, criterion_main, Criterion}; use rand::prelude::*; use rand::SeedableRng; +use rand_pcg::Pcg32; criterion_group!( -name = benches; -config = Criterion::default(); -targets = bench + name = benches; + config = Criterion::default(); + targets = bench ); criterion_main!(benches); pub fn bench(c: &mut Criterion) { + c.bench_function("seq_shuffle_100", |b| { + let mut rng = Pcg32::from_rng(thread_rng()); + let mut buf = [0i32; 100]; + rng.fill(&mut buf); + let x = black_box(&mut buf); + b.iter(|| { + x.shuffle(&mut rng); + x[0] + }) + }); + bench_rng::(c, "ChaCha12"); bench_rng::(c, "Pcg32"); bench_rng::(c, "Pcg64"); @@ -34,17 +47,15 @@ fn bench_rng(c: &mut Criterion, rng_name: &'static s }); if length >= 10 { - c.bench_function( - format!("partial_shuffle_{length}_{rng_name}").as_str(), - |b| { - let mut rng = Rng::seed_from_u64(123); - let mut vec: Vec = (0..length).collect(); - b.iter(|| { - vec.partial_shuffle(&mut rng, length / 2); - vec[0] - }) - }, - ); + let name = format!("partial_shuffle_{length}_{rng_name}"); + c.bench_function(name.as_str(), |b| { + let mut rng = Rng::seed_from_u64(123); + let mut vec: Vec = (0..length).collect(); + b.iter(|| { + vec.partial_shuffle(&mut rng, length / 2); + vec[0] + }) + }); } } } diff --git a/benches/benches/standard.rs b/benches/benches/standard.rs new file mode 100644 index 00000000..1e7fadc7 --- /dev/null +++ b/benches/benches/standard.rs @@ -0,0 +1,64 @@ +// Copyright 2019 Developers of the Rand project. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use core::time::Duration; +use criterion::measurement::WallTime; +use criterion::{criterion_group, criterion_main, BenchmarkGroup, Criterion}; +use rand::distr::{Alphanumeric, Standard}; +use rand::prelude::*; +use rand_distr::{Open01, OpenClosed01}; +use rand_pcg::Pcg64Mcg; + +criterion_group!( + name = benches; + config = Criterion::default(); + targets = bench +); +criterion_main!(benches); + +fn bench_ty(g: &mut BenchmarkGroup, name: &str) +where + D: Distribution + Default, +{ + g.throughput(criterion::Throughput::Bytes(size_of::() as u64)); + g.bench_function(name, |b| { + let mut rng = Pcg64Mcg::from_os_rng(); + + b.iter(|| rng.sample::(D::default())); + }); +} + +pub fn bench(c: &mut Criterion) { + let mut g = c.benchmark_group("Standard"); + g.sample_size(1000); + g.warm_up_time(Duration::from_millis(500)); + g.measurement_time(Duration::from_millis(1000)); + + macro_rules! do_ty { + ($t:ty) => { + bench_ty::<$t, Standard>(&mut g, stringify!($t)); + }; + ($t:ty, $($tt:ty),*) => { + do_ty!($t); + do_ty!($($tt),*); + }; + } + + do_ty!(i8, i16, i32, i64, i128, isize); + do_ty!(f32, f64); + do_ty!(char); + + bench_ty::(&mut g, "Alphanumeric"); + + bench_ty::(&mut g, "Open01/f32"); + bench_ty::(&mut g, "Open01/f64"); + bench_ty::(&mut g, "OpenClosed01/f32"); + bench_ty::(&mut g, "OpenClosed01/f64"); + + g.finish(); +} diff --git a/benches/src/uniform.rs b/benches/benches/uniform.rs similarity index 100% rename from benches/src/uniform.rs rename to benches/benches/uniform.rs diff --git a/benches/src/uniform_float.rs b/benches/benches/uniform_float.rs similarity index 100% rename from benches/src/uniform_float.rs rename to benches/benches/uniform_float.rs diff --git a/benches/benches/weighted.rs b/benches/benches/weighted.rs index da437ab5..21e66ebe 100644 --- a/benches/benches/weighted.rs +++ b/benches/benches/weighted.rs @@ -6,31 +6,55 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![feature(test)] - -extern crate test; - +use criterion::{black_box, criterion_group, criterion_main, Criterion}; use rand::distr::WeightedIndex; -use rand::Rng; -use test::Bencher; +use rand::prelude::*; +use rand::seq::index::sample_weighted; -#[bench] -fn weighted_index_creation(b: &mut Bencher) { - let mut rng = rand::thread_rng(); - let weights = [1u32, 2, 4, 0, 5, 1, 7, 1, 2, 3, 4, 5, 6, 7]; - b.iter(|| { - let distr = WeightedIndex::new(weights.to_vec()).unwrap(); - rng.sample(distr) - }) -} +criterion_group!( + name = benches; + config = Criterion::default(); + targets = bench +); +criterion_main!(benches); -#[bench] -fn weighted_index_modification(b: &mut Bencher) { - let mut rng = rand::thread_rng(); - let weights = [1u32, 2, 3, 0, 5, 6, 7, 1, 2, 3, 4, 5, 6, 7]; - let mut distr = WeightedIndex::new(weights.to_vec()).unwrap(); - b.iter(|| { - distr.update_weights(&[(2, &4), (5, &1)]).unwrap(); - rng.sample(&distr) - }) +pub fn bench(c: &mut Criterion) { + c.bench_function("weighted_index_creation", |b| { + let mut rng = rand::thread_rng(); + let weights = black_box([1u32, 2, 4, 0, 5, 1, 7, 1, 2, 3, 4, 5, 6, 7]); + b.iter(|| { + let distr = WeightedIndex::new(weights.to_vec()).unwrap(); + rng.sample(distr) + }) + }); + + c.bench_function("weighted_index_modification", |b| { + let mut rng = rand::thread_rng(); + let weights = black_box([1u32, 2, 3, 0, 5, 6, 7, 1, 2, 3, 4, 5, 6, 7]); + let mut distr = WeightedIndex::new(weights.to_vec()).unwrap(); + b.iter(|| { + distr.update_weights(&[(2, &4), (5, &1)]).unwrap(); + rng.sample(&distr) + }) + }); + + let lens = [ + (1, 1000, "1k"), + (10, 1000, "1k"), + (100, 1000, "1k"), + (100, 1_000_000, "1M"), + (200, 1_000_000, "1M"), + (400, 1_000_000, "1M"), + (600, 1_000_000, "1M"), + (1000, 1_000_000, "1M"), + ]; + for (amount, length, len_name) in lens { + let name = format!("weighted_sample_indices_{}_of_{}", amount, len_name); + c.bench_function(name.as_str(), |b| { + let length = black_box(length); + let amount = black_box(amount); + let mut rng = SmallRng::from_rng(thread_rng()); + b.iter(|| sample_weighted(&mut rng, length, |idx| (1 + (idx % 100)) as u32, amount)) + }); + } } diff --git a/benches/src/seq_choose.rs b/benches/src/seq_choose.rs deleted file mode 100644 index ccf7e582..00000000 --- a/benches/src/seq_choose.rs +++ /dev/null @@ -1,111 +0,0 @@ -// Copyright 2018-2023 Developers of the Rand project. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. -use criterion::{black_box, criterion_group, criterion_main, Criterion}; -use rand::prelude::*; -use rand::SeedableRng; - -criterion_group!( -name = benches; -config = Criterion::default(); -targets = bench -); -criterion_main!(benches); - -pub fn bench(c: &mut Criterion) { - bench_rng::(c, "ChaCha20"); - bench_rng::(c, "Pcg32"); - bench_rng::(c, "Pcg64"); -} - -fn bench_rng(c: &mut Criterion, rng_name: &'static str) { - for length in [1, 2, 3, 10, 100, 1000].map(black_box) { - c.bench_function( - format!("choose_size-hinted_from_{length}_{rng_name}").as_str(), - |b| { - let mut rng = Rng::seed_from_u64(123); - b.iter(|| choose_size_hinted(length, &mut rng)) - }, - ); - - c.bench_function( - format!("choose_stable_from_{length}_{rng_name}").as_str(), - |b| { - let mut rng = Rng::seed_from_u64(123); - b.iter(|| choose_stable(length, &mut rng)) - }, - ); - - c.bench_function( - format!("choose_unhinted_from_{length}_{rng_name}").as_str(), - |b| { - let mut rng = Rng::seed_from_u64(123); - b.iter(|| choose_unhinted(length, &mut rng)) - }, - ); - - c.bench_function( - format!("choose_windowed_from_{length}_{rng_name}").as_str(), - |b| { - let mut rng = Rng::seed_from_u64(123); - b.iter(|| choose_windowed(length, 7, &mut rng)) - }, - ); - } -} - -fn choose_size_hinted(max: usize, rng: &mut R) -> Option { - let iterator = 0..max; - iterator.choose(rng) -} - -fn choose_stable(max: usize, rng: &mut R) -> Option { - let iterator = 0..max; - iterator.choose_stable(rng) -} - -fn choose_unhinted(max: usize, rng: &mut R) -> Option { - let iterator = UnhintedIterator { iter: (0..max) }; - iterator.choose(rng) -} - -fn choose_windowed(max: usize, window_size: usize, rng: &mut R) -> Option { - let iterator = WindowHintedIterator { - iter: (0..max), - window_size, - }; - iterator.choose(rng) -} - -#[derive(Clone)] -struct UnhintedIterator { - iter: I, -} -impl Iterator for UnhintedIterator { - type Item = I::Item; - - fn next(&mut self) -> Option { - self.iter.next() - } -} - -#[derive(Clone)] -struct WindowHintedIterator { - iter: I, - window_size: usize, -} -impl Iterator for WindowHintedIterator { - type Item = I::Item; - - fn next(&mut self) -> Option { - self.iter.next() - } - - fn size_hint(&self) -> (usize, Option) { - (core::cmp::min(self.iter.len(), self.window_size), None) - } -} diff --git a/src/distr/float.rs b/src/distr/float.rs index 0732b0af..a8cbc96b 100644 --- a/src/distr/float.rs +++ b/src/distr/float.rs @@ -42,7 +42,7 @@ use serde::{Deserialize, Serialize}; /// [`Standard`]: crate::distr::Standard /// [`Open01`]: crate::distr::Open01 /// [`Uniform`]: crate::distr::uniform::Uniform -#[derive(Clone, Copy, Debug)] +#[derive(Clone, Copy, Debug, Default)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct OpenClosed01; @@ -69,7 +69,7 @@ pub struct OpenClosed01; /// [`Standard`]: crate::distr::Standard /// [`OpenClosed01`]: crate::distr::OpenClosed01 /// [`Uniform`]: crate::distr::uniform::Uniform -#[derive(Clone, Copy, Debug)] +#[derive(Clone, Copy, Debug, Default)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct Open01; diff --git a/src/distr/mod.rs b/src/distr/mod.rs index 668c9ffd..716aa417 100644 --- a/src/distr/mod.rs +++ b/src/distr/mod.rs @@ -217,6 +217,6 @@ use crate::Rng; /// [`f32x4`]: std::simd::f32x4 /// [`mask32x4`]: std::simd::mask32x4 /// [`simd_support`]: https://github.com/rust-random/rand#crate-features -#[derive(Clone, Copy, Debug)] +#[derive(Clone, Copy, Debug, Default)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct Standard; diff --git a/src/distr/other.rs b/src/distr/other.rs index ef10828d..b2e91e53 100644 --- a/src/distr/other.rs +++ b/src/distr/other.rs @@ -66,7 +66,7 @@ use serde::{Deserialize, Serialize}; /// /// - [Wikipedia article on Password Strength](https://en.wikipedia.org/wiki/Password_strength) /// - [Diceware for generating memorable passwords](https://en.wikipedia.org/wiki/Diceware) -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, Default)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct Alphanumeric;