Migrate remaining benchmarks to Criterion (#1490)

Translate everything still using the old test harness to Criterion.
This commit is contained in:
Diggory Hardy 2024-09-06 09:07:47 +01:00 committed by GitHub
parent 79f1b0ffdb
commit 9e030aa221
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
20 changed files with 619 additions and 1031 deletions

View File

@ -23,4 +23,4 @@ jobs:
- name: Clippy - name: Clippy
run: cargo clippy --all-targets -- -D warnings run: cargo clippy --all-targets -- -D warnings
- name: Build - name: Build
run: RUSTFLAGS=-Dwarnings cargo build --all-targets run: RUSTFLAGS=-Dwarnings cargo test --benches

View File

@ -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) - Add `IndexedRandom::choose_multiple_array`, `index::sample_array` (#1453, #1469)
- Bump the MSRV to 1.61.0 - 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) - 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) - Annotate panicking methods with `#[track_caller]` (#1442, #1447)
- Enable feature `small_rng` by default (#1455) - Enable feature `small_rng` by default (#1455)
- Allow `UniformFloat::new` samples and `UniformFloat::sample_single` to yield `high` (#1462) - Allow `UniformFloat::new` samples and `UniformFloat::sample_single` to yield `high` (#1462)

View File

@ -15,26 +15,41 @@ criterion = "0.5"
criterion-cycles-per-byte = "0.6" criterion-cycles-per-byte = "0.6"
[[bench]] [[bench]]
name = "distr" name = "array"
path = "src/distr.rs"
harness = false harness = false
[[bench]] [[bench]]
name = "uniform" name = "bool"
path = "src/uniform.rs" harness = false
[[bench]]
name = "distr"
harness = false
[[bench]]
name = "generators"
harness = false harness = false
[[bench]] [[bench]]
name = "seq_choose" name = "seq_choose"
path = "src/seq_choose.rs"
harness = false harness = false
[[bench]] [[bench]]
name = "shuffle" name = "shuffle"
path = "src/shuffle.rs" harness = false
[[bench]]
name = "standard"
harness = false
[[bench]]
name = "uniform"
harness = false harness = false
[[bench]] [[bench]]
name = "uniform_float" name = "uniform_float"
path = "src/uniform_float.rs" harness = false
[[bench]]
name = "weighted"
harness = false harness = false

94
benches/benches/array.rs Normal file
View File

@ -0,0 +1,94 @@
// Copyright 2018-2023 Developers of the Rand project.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or https://opensource.org/licenses/MIT>, 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<u16> = 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<u16> = 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<u64> = 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<u64> = 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
});
});
}

View File

@ -1,440 +0,0 @@
// Copyright 2018 Developers of the Rand project.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or https://opensource.org/licenses/MIT>, 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::<Duration>() 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 FisherYates 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);

69
benches/benches/bool.rs Normal file
View File

@ -0,0 +1,69 @@
// Copyright 2018 Developers of the Rand project.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or https://opensource.org/licenses/MIT>, 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::<bool, _>(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))
});
}

View File

@ -13,8 +13,7 @@
const RAND_BENCH_N: u64 = 1000; const RAND_BENCH_N: u64 = 1000;
use criterion::{criterion_group, criterion_main, Criterion, use criterion::{criterion_group, criterion_main, Criterion, Throughput};
Throughput};
use criterion_cycles_per_byte::CyclesPerByte; use criterion_cycles_per_byte::CyclesPerByte;
use core::mem::size_of; use core::mem::size_of;

View File

@ -6,139 +6,160 @@
// option. This file may not be copied, modified, or distributed // option. This file may not be copied, modified, or distributed
// except according to those terms. // except according to those terms.
#![feature(test)] use core::time::Duration;
#![allow(non_snake_case)] use criterion::measurement::WallTime;
use criterion::{black_box, criterion_group, criterion_main, BenchmarkGroup, Criterion};
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 rand::prelude::*; use rand::prelude::*;
use rand::rngs::ReseedingRng; use rand::rngs::ReseedingRng;
use rand::rngs::{mock::StepRng, OsRng}; use rand::rngs::{mock::StepRng, OsRng};
use rand_chacha::rand_core::UnwrapErr;
use rand_chacha::{ChaCha12Rng, ChaCha20Core, ChaCha20Rng, ChaCha8Rng}; use rand_chacha::{ChaCha12Rng, ChaCha20Core, ChaCha20Rng, ChaCha8Rng};
use rand_pcg::{Pcg32, Pcg64, Pcg64Dxsm, Pcg64Mcg}; use rand_pcg::{Pcg32, Pcg64, Pcg64Dxsm, Pcg64Mcg};
macro_rules! gen_bytes { criterion_group!(
($fnn:ident, $gen:expr) => { name = benches;
#[bench] config = Criterion::default();
fn $fnn(b: &mut Bencher) { targets = gen_bytes, gen_u32, gen_u64, init_gen, reseeding_bytes
let mut rng = $gen; );
let mut buf = [0u8; BYTES_LEN]; 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<WallTime>, name: &str, mut rng: impl Rng) {
g.bench_function(name, |b| {
let mut buf = [0u8; 1024];
b.iter(|| { b.iter(|| {
for _ in 0..RAND_BENCH_N { rng.fill_bytes(&mut buf);
rng.fill_bytes(&mut buf); black_box(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)); pub fn gen_u32(c: &mut Criterion) {
gen_bytes!(gen_bytes_pcg32, Pcg32::from_os_rng()); let mut g = c.benchmark_group("gen_u32");
gen_bytes!(gen_bytes_pcg64, Pcg64::from_os_rng()); g.sample_size(1000);
gen_bytes!(gen_bytes_pcg64mcg, Pcg64Mcg::from_os_rng()); g.warm_up_time(Duration::from_millis(500));
gen_bytes!(gen_bytes_pcg64dxsm, Pcg64Dxsm::from_os_rng()); g.measurement_time(Duration::from_millis(1000));
gen_bytes!(gen_bytes_chacha8, ChaCha8Rng::from_os_rng()); g.throughput(criterion::Throughput::Bytes(4));
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());
macro_rules! gen_uint { fn bench(g: &mut BenchmarkGroup<WallTime>, name: &str, mut rng: impl Rng) {
($fnn:ident, $ty:ty, $gen:expr) => { g.bench_function(name, |b| {
#[bench] b.iter(|| rng.random::<u32>());
fn $fnn(b: &mut Bencher) { });
let mut rng = $gen; }
b.iter(|| {
let mut accum: $ty = 0; bench(&mut g, "step", StepRng::new(0, 1));
for _ in 0..RAND_BENCH_N { bench(&mut g, "pcg32", Pcg32::from_os_rng());
accum = accum.wrapping_add(rng.random::<$ty>()); bench(&mut g, "pcg64", Pcg64::from_os_rng());
} bench(&mut g, "pcg64mcg", Pcg64Mcg::from_os_rng());
accum bench(&mut g, "pcg64dxsm", Pcg64Dxsm::from_os_rng());
}); bench(&mut g, "chacha8", ChaCha8Rng::from_os_rng());
b.bytes = size_of::<$ty>() as u64 * RAND_BENCH_N; 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)); pub fn gen_u64(c: &mut Criterion) {
gen_uint!(gen_u32_pcg32, u32, Pcg32::from_os_rng()); let mut g = c.benchmark_group("gen_u64");
gen_uint!(gen_u32_pcg64, u32, Pcg64::from_os_rng()); g.sample_size(1000);
gen_uint!(gen_u32_pcg64mcg, u32, Pcg64Mcg::from_os_rng()); g.warm_up_time(Duration::from_millis(500));
gen_uint!(gen_u32_pcg64dxsm, u32, Pcg64Dxsm::from_os_rng()); g.measurement_time(Duration::from_millis(1000));
gen_uint!(gen_u32_chacha8, u32, ChaCha8Rng::from_os_rng()); g.throughput(criterion::Throughput::Bytes(8));
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());
gen_uint!(gen_u64_step, u64, StepRng::new(0, 1)); fn bench(g: &mut BenchmarkGroup<WallTime>, name: &str, mut rng: impl Rng) {
gen_uint!(gen_u64_pcg32, u64, Pcg32::from_os_rng()); g.bench_function(name, |b| {
gen_uint!(gen_u64_pcg64, u64, Pcg64::from_os_rng()); b.iter(|| rng.random::<u64>());
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());
macro_rules! init_gen { bench(&mut g, "step", StepRng::new(0, 1));
($fnn:ident, $gen:ident) => { bench(&mut g, "pcg32", Pcg32::from_os_rng());
#[bench] bench(&mut g, "pcg64", Pcg64::from_os_rng());
fn $fnn(b: &mut Bencher) { 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<R: SeedableRng>(g: &mut BenchmarkGroup<WallTime>, name: &str) {
g.bench_function(name, |b| {
let mut rng = Pcg32::from_os_rng(); let mut rng = Pcg32::from_os_rng();
b.iter(|| { b.iter(|| R::from_rng(&mut rng));
let r2 = $gen::from_rng(&mut rng); });
r2 }
});
} bench::<Pcg32>(&mut g, "pcg32");
}; bench::<Pcg64>(&mut g, "pcg64");
bench::<Pcg64Mcg>(&mut g, "pcg64mcg");
bench::<Pcg64Dxsm>(&mut g, "pcg64dxsm");
bench::<ChaCha8Rng>(&mut g, "chacha8");
bench::<ChaCha12Rng>(&mut g, "chacha12");
bench::<ChaCha20Rng>(&mut g, "chacha20");
bench::<StdRng>(&mut g, "std");
g.finish()
} }
init_gen!(init_pcg32, Pcg32); pub fn reseeding_bytes(c: &mut Criterion) {
init_gen!(init_pcg64, Pcg64); let mut g = c.benchmark_group("reseeding_bytes");
init_gen!(init_pcg64mcg, Pcg64Mcg); g.warm_up_time(Duration::from_millis(500));
init_gen!(init_pcg64dxsm, Pcg64Dxsm); g.throughput(criterion::Throughput::Bytes(1024 * 1024));
init_gen!(init_chacha, ChaCha20Rng);
const RESEEDING_BYTES_LEN: usize = 1024 * 1024; fn bench(g: &mut BenchmarkGroup<WallTime>, thresh: u64) {
const RESEEDING_BENCH_N: u64 = 16; let name = format!("chacha20_{}k", thresh);
g.bench_function(name.as_str(), |b| {
macro_rules! reseeding_bytes { let mut rng = ReseedingRng::new(ChaCha20Core::from_os_rng(), thresh * 1024, OsRng);
($fnn:ident, $thresh:expr) => { let mut buf = [0u8; 1024 * 1024];
#[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];
b.iter(|| { b.iter(|| {
for _ in 0..RESEEDING_BENCH_N { rng.fill_bytes(&mut buf);
rng.fill_bytes(&mut buf); black_box(&buf);
black_box(&buf);
}
}); });
b.bytes = RESEEDING_BYTES_LEN as u64 * RESEEDING_BENCH_N; });
} }
};
}
reseeding_bytes!(reseeding_chacha20_4k, 4); bench(&mut g, 4);
reseeding_bytes!(reseeding_chacha20_16k, 16); bench(&mut g, 16);
reseeding_bytes!(reseeding_chacha20_32k, 32); bench(&mut g, 32);
reseeding_bytes!(reseeding_chacha20_64k, 64); bench(&mut g, 64);
reseeding_bytes!(reseeding_chacha20_256k, 256); bench(&mut g, 256);
reseeding_bytes!(reseeding_chacha20_1M, 1024); bench(&mut g, 1024);
g.finish()
}

View File

@ -1,183 +0,0 @@
// Copyright 2018 Developers of the Rand project.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or https://opensource.org/licenses/MIT>, 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<u16> = 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<u16> = 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<u64> = 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<u64> = 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;
}

View File

@ -1,130 +0,0 @@
// Copyright 2018 Developers of the Rand project.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or https://opensource.org/licenses/MIT>, 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::<usize>() 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);

View File

@ -0,0 +1,155 @@
// Copyright 2018-2023 Developers of the Rand project.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or https://opensource.org/licenses/MIT>, 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::<rand_chacha::ChaCha20Rng>(c, "ChaCha20");
bench_rng::<rand_pcg::Pcg32>(c, "Pcg32");
bench_rng::<rand_pcg::Pcg64>(c, "Pcg64");
}
fn bench_rng<Rng: RngCore + SeedableRng>(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<R: Rng>(max: usize, rng: &mut R) -> Option<usize> {
let iterator = 0..max;
iterator.choose(rng)
}
fn choose_stable<R: Rng>(max: usize, rng: &mut R) -> Option<usize> {
let iterator = 0..max;
iterator.choose_stable(rng)
}
fn choose_unhinted<R: Rng>(max: usize, rng: &mut R) -> Option<usize> {
let iterator = UnhintedIterator { iter: (0..max) };
iterator.choose(rng)
}
fn choose_windowed<R: Rng>(max: usize, window_size: usize, rng: &mut R) -> Option<usize> {
let iterator = WindowHintedIterator {
iter: (0..max),
window_size,
};
iterator.choose(rng)
}
#[derive(Clone)]
struct UnhintedIterator<I: Iterator + Clone> {
iter: I,
}
impl<I: Iterator + Clone> Iterator for UnhintedIterator<I> {
type Item = I::Item;
fn next(&mut self) -> Option<Self::Item> {
self.iter.next()
}
}
#[derive(Clone)]
struct WindowHintedIterator<I: ExactSizeIterator + Iterator + Clone> {
iter: I,
window_size: usize,
}
impl<I: ExactSizeIterator + Iterator + Clone> Iterator for WindowHintedIterator<I> {
type Item = I::Item;
fn next(&mut self) -> Option<Self::Item> {
self.iter.next()
}
fn size_hint(&self) -> (usize, Option<usize>) {
(core::cmp::min(self.iter.len(), self.window_size), None)
}
}

View File

@ -5,18 +5,31 @@
// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your // <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed // option. This file may not be copied, modified, or distributed
// except according to those terms. // except according to those terms.
use criterion::{black_box, criterion_group, criterion_main, Criterion}; use criterion::{black_box, criterion_group, criterion_main, Criterion};
use rand::prelude::*; use rand::prelude::*;
use rand::SeedableRng; use rand::SeedableRng;
use rand_pcg::Pcg32;
criterion_group!( criterion_group!(
name = benches; name = benches;
config = Criterion::default(); config = Criterion::default();
targets = bench targets = bench
); );
criterion_main!(benches); criterion_main!(benches);
pub fn bench(c: &mut Criterion) { 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::<rand_chacha::ChaCha12Rng>(c, "ChaCha12"); bench_rng::<rand_chacha::ChaCha12Rng>(c, "ChaCha12");
bench_rng::<rand_pcg::Pcg32>(c, "Pcg32"); bench_rng::<rand_pcg::Pcg32>(c, "Pcg32");
bench_rng::<rand_pcg::Pcg64>(c, "Pcg64"); bench_rng::<rand_pcg::Pcg64>(c, "Pcg64");
@ -34,17 +47,15 @@ fn bench_rng<Rng: RngCore + SeedableRng>(c: &mut Criterion, rng_name: &'static s
}); });
if length >= 10 { if length >= 10 {
c.bench_function( let name = format!("partial_shuffle_{length}_{rng_name}");
format!("partial_shuffle_{length}_{rng_name}").as_str(), c.bench_function(name.as_str(), |b| {
|b| { let mut rng = Rng::seed_from_u64(123);
let mut rng = Rng::seed_from_u64(123); let mut vec: Vec<usize> = (0..length).collect();
let mut vec: Vec<usize> = (0..length).collect(); b.iter(|| {
b.iter(|| { vec.partial_shuffle(&mut rng, length / 2);
vec.partial_shuffle(&mut rng, length / 2); vec[0]
vec[0] })
}) });
},
);
} }
} }
} }

View File

@ -0,0 +1,64 @@
// Copyright 2019 Developers of the Rand project.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or https://opensource.org/licenses/MIT>, 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<T, D>(g: &mut BenchmarkGroup<WallTime>, name: &str)
where
D: Distribution<T> + Default,
{
g.throughput(criterion::Throughput::Bytes(size_of::<T>() as u64));
g.bench_function(name, |b| {
let mut rng = Pcg64Mcg::from_os_rng();
b.iter(|| rng.sample::<T, _>(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::<u8, Alphanumeric>(&mut g, "Alphanumeric");
bench_ty::<f32, Open01>(&mut g, "Open01/f32");
bench_ty::<f64, Open01>(&mut g, "Open01/f64");
bench_ty::<f32, OpenClosed01>(&mut g, "OpenClosed01/f32");
bench_ty::<f64, OpenClosed01>(&mut g, "OpenClosed01/f64");
g.finish();
}

View File

@ -6,31 +6,55 @@
// option. This file may not be copied, modified, or distributed // option. This file may not be copied, modified, or distributed
// except according to those terms. // except according to those terms.
#![feature(test)] use criterion::{black_box, criterion_group, criterion_main, Criterion};
extern crate test;
use rand::distr::WeightedIndex; use rand::distr::WeightedIndex;
use rand::Rng; use rand::prelude::*;
use test::Bencher; use rand::seq::index::sample_weighted;
#[bench] criterion_group!(
fn weighted_index_creation(b: &mut Bencher) { name = benches;
let mut rng = rand::thread_rng(); config = Criterion::default();
let weights = [1u32, 2, 4, 0, 5, 1, 7, 1, 2, 3, 4, 5, 6, 7]; targets = bench
b.iter(|| { );
let distr = WeightedIndex::new(weights.to_vec()).unwrap(); criterion_main!(benches);
rng.sample(distr)
})
}
#[bench] pub fn bench(c: &mut Criterion) {
fn weighted_index_modification(b: &mut Bencher) { c.bench_function("weighted_index_creation", |b| {
let mut rng = rand::thread_rng(); let mut rng = rand::thread_rng();
let weights = [1u32, 2, 3, 0, 5, 6, 7, 1, 2, 3, 4, 5, 6, 7]; let weights = black_box([1u32, 2, 4, 0, 5, 1, 7, 1, 2, 3, 4, 5, 6, 7]);
let mut distr = WeightedIndex::new(weights.to_vec()).unwrap(); b.iter(|| {
b.iter(|| { let distr = WeightedIndex::new(weights.to_vec()).unwrap();
distr.update_weights(&[(2, &4), (5, &1)]).unwrap(); rng.sample(distr)
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))
});
}
} }

View File

@ -1,111 +0,0 @@
// Copyright 2018-2023 Developers of the Rand project.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or https://opensource.org/licenses/MIT>, 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::<rand_chacha::ChaCha20Rng>(c, "ChaCha20");
bench_rng::<rand_pcg::Pcg32>(c, "Pcg32");
bench_rng::<rand_pcg::Pcg64>(c, "Pcg64");
}
fn bench_rng<Rng: RngCore + SeedableRng>(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<R: Rng>(max: usize, rng: &mut R) -> Option<usize> {
let iterator = 0..max;
iterator.choose(rng)
}
fn choose_stable<R: Rng>(max: usize, rng: &mut R) -> Option<usize> {
let iterator = 0..max;
iterator.choose_stable(rng)
}
fn choose_unhinted<R: Rng>(max: usize, rng: &mut R) -> Option<usize> {
let iterator = UnhintedIterator { iter: (0..max) };
iterator.choose(rng)
}
fn choose_windowed<R: Rng>(max: usize, window_size: usize, rng: &mut R) -> Option<usize> {
let iterator = WindowHintedIterator {
iter: (0..max),
window_size,
};
iterator.choose(rng)
}
#[derive(Clone)]
struct UnhintedIterator<I: Iterator + Clone> {
iter: I,
}
impl<I: Iterator + Clone> Iterator for UnhintedIterator<I> {
type Item = I::Item;
fn next(&mut self) -> Option<Self::Item> {
self.iter.next()
}
}
#[derive(Clone)]
struct WindowHintedIterator<I: ExactSizeIterator + Iterator + Clone> {
iter: I,
window_size: usize,
}
impl<I: ExactSizeIterator + Iterator + Clone> Iterator for WindowHintedIterator<I> {
type Item = I::Item;
fn next(&mut self) -> Option<Self::Item> {
self.iter.next()
}
fn size_hint(&self) -> (usize, Option<usize>) {
(core::cmp::min(self.iter.len(), self.window_size), None)
}
}

View File

@ -42,7 +42,7 @@ use serde::{Deserialize, Serialize};
/// [`Standard`]: crate::distr::Standard /// [`Standard`]: crate::distr::Standard
/// [`Open01`]: crate::distr::Open01 /// [`Open01`]: crate::distr::Open01
/// [`Uniform`]: crate::distr::uniform::Uniform /// [`Uniform`]: crate::distr::uniform::Uniform
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy, Debug, Default)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct OpenClosed01; pub struct OpenClosed01;
@ -69,7 +69,7 @@ pub struct OpenClosed01;
/// [`Standard`]: crate::distr::Standard /// [`Standard`]: crate::distr::Standard
/// [`OpenClosed01`]: crate::distr::OpenClosed01 /// [`OpenClosed01`]: crate::distr::OpenClosed01
/// [`Uniform`]: crate::distr::uniform::Uniform /// [`Uniform`]: crate::distr::uniform::Uniform
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy, Debug, Default)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Open01; pub struct Open01;

View File

@ -217,6 +217,6 @@ use crate::Rng;
/// [`f32x4`]: std::simd::f32x4 /// [`f32x4`]: std::simd::f32x4
/// [`mask32x4`]: std::simd::mask32x4 /// [`mask32x4`]: std::simd::mask32x4
/// [`simd_support`]: https://github.com/rust-random/rand#crate-features /// [`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))] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Standard; pub struct Standard;

View File

@ -66,7 +66,7 @@ use serde::{Deserialize, Serialize};
/// ///
/// - [Wikipedia article on Password Strength](https://en.wikipedia.org/wiki/Password_strength) /// - [Wikipedia article on Password Strength](https://en.wikipedia.org/wiki/Password_strength)
/// - [Diceware for generating memorable passwords](https://en.wikipedia.org/wiki/Diceware) /// - [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))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Alphanumeric; pub struct Alphanumeric;