Files
rand/benches/distributions.rs
T
Vinzent Steinberg bbb0dff53c Fix benchmarks
2020-08-05 13:25:25 +02:00

441 lines
18 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// 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::distributions::{Alphanumeric, Open01, OpenClosed01, Standard, Uniform};
use rand::distributions::uniform::{UniformInt, UniformSampler};
use std::mem::size_of;
use std::num::{NonZeroU128, NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU8};
use std::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_entropy();
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_entropy();
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_entropy();
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_entropy();
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_value(), 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_entropy();
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));
distr_int!(distr_uniform_i16, i16, Uniform::new(-500i16, 2000));
distr_int!(distr_uniform_i32, i32, Uniform::new(-200_000_000i32, 800_000_000));
distr_int!(distr_uniform_i64, i64, Uniform::new(3i64, 123_456_789_123));
distr_int!(distr_uniform_i128, i128, Uniform::new(-123_456_789_123i128, 123_456_789_123_456_789));
distr_int!(distr_uniform_usize16, usize, Uniform::new(0usize, 0xb9d7));
distr_int!(distr_uniform_usize32, usize, Uniform::new(0usize, 0x548c0f43));
#[cfg(target_pointer_width = "64")]
distr_int!(distr_uniform_usize64, usize, Uniform::new(0usize, 0x3a42714f2bf927a8));
distr_int!(distr_uniform_isize, isize, Uniform::new(-1060478432isize, 1858574057));
distr_float!(distr_uniform_f32, f32, Uniform::new(2.26f32, 2.319));
distr_float!(distr_uniform_f64, f64, Uniform::new(2.26f64, 2.319));
const LARGE_SEC: u64 = u64::max_value() / 1000;
distr_duration!(distr_uniform_duration_largest,
Uniform::new_inclusive(Duration::new(0, 0), Duration::new(u64::max_value(), 999_999_999))
);
distr_duration!(distr_uniform_duration_large,
Uniform::new(Duration::new(0, 0), Duration::new(LARGE_SEC, 1_000_000_000 / 2))
);
distr_duration!(distr_uniform_duration_one,
Uniform::new(Duration::new(0, 0), Duration::new(1, 0))
);
distr_duration!(distr_uniform_duration_variety,
Uniform::new(Duration::new(10000, 423423), Duration::new(200000, 6969954))
);
distr_duration!(distr_uniform_duration_edge,
Uniform::new_inclusive(Duration::new(LARGE_SEC, 999_999_999), Duration::new(LARGE_SEC + 1, 1))
);
// 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_entropy();
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) & std::$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_entropy();
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/distributions/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_entropy();
let low = black_box($low);
let high = black_box($high);
b.iter(|| {
for _ in 0..10 {
let dist = UniformInt::<$type>::new(low, high);
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_entropy();
let low = black_box($low);
let high = black_box($high);
b.iter(|| {
for _ in 0..10 {
let dist = UniformInt::<$type>::new_inclusive(low, high);
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_entropy();
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));
}
});
}
};
}
// 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_value(), 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_value(), 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_value(), 10);
uniform_sample!(uniform_u16x10_allm1_new, u16, 0, u16::max_value(), 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_value(), 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_value(), 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_value(), 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_value(), 10);
uniform_sample!(uniform_u32x10_allm1_new, u32, 0, u32::max_value(), 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_value(), 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_value(), 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_value(), 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_value(), 10);
uniform_sample!(uniform_u64x10_allm1_new, u64, 0, u64::max_value(), 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_value(), 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_value(), 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_value(), 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_value(), 10);
uniform_sample!(uniform_u128x10_allm1_new, u128, 0, u128::max_value(), 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_value(), 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);