From 2e1265fecf9e1e1fb8dc8410b3909b543567da41 Mon Sep 17 00:00:00 2001 From: Pazzaz Date: Wed, 1 Aug 2018 19:52:53 +0200 Subject: [PATCH 1/6] Add uniform Duration benches --- benches/distributions.rs | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/benches/distributions.rs b/benches/distributions.rs index d5ead90d5b1..6713e7c027a 100644 --- a/benches/distributions.rs +++ b/benches/distributions.rs @@ -9,6 +9,7 @@ const RAND_BENCH_N: u64 = 1000; use std::mem::size_of; use test::Bencher; +use std::time::Duration; use rand::{Rng, FromEntropy}; use rand::rngs::SmallRng; @@ -54,6 +55,26 @@ macro_rules! distr_float { } } +macro_rules! distr_duration { + ($fnn:ident, $distr:expr) => { + #[bench] + fn $fnn(b: &mut Bencher) { + let mut rng = SmallRng::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::() as u64 * ::RAND_BENCH_N; + } + } +} + macro_rules! distr { ($fnn:ident, $ty:ty, $distr:expr) => { #[bench] @@ -85,6 +106,12 @@ distr_int!(distr_uniform_i128, i128, Uniform::new(-123_456_789_123i128, 123_456_ distr_float!(distr_uniform_f32, f32, Uniform::new(2.26f32, 2.319)); distr_float!(distr_uniform_f64, f64, Uniform::new(2.26f64, 2.319)); +distr_duration!(distr_uniform_duration_largest, Uniform::new(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(u64::max_value()/1000, 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))); + + // standard distr_int!(distr_standard_i8, i8, Standard); distr_int!(distr_standard_i16, i16, Standard); From fbab1a118dbc7422cadfa181af074cf381375e23 Mon Sep 17 00:00:00 2001 From: Pazzaz Date: Sun, 5 Aug 2018 19:38:59 +0200 Subject: [PATCH 2/6] Don't use an offset in UniformDuration --- src/distributions/uniform.rs | 75 ++++++++++++++++++++++-------------- 1 file changed, 47 insertions(+), 28 deletions(-) diff --git a/src/distributions/uniform.rs b/src/distributions/uniform.rs index 9a0acd018ac..87c4322c52a 100644 --- a/src/distributions/uniform.rs +++ b/src/distributions/uniform.rs @@ -693,7 +693,6 @@ uniform_float_impl! { f64x8, u64x8, f64, u64, 64 - 52 } #[cfg(feature = "std")] #[derive(Clone, Copy, Debug)] pub struct UniformDuration { - offset: Duration, mode: UniformDurationMode, } @@ -701,10 +700,17 @@ pub struct UniformDuration { #[derive(Debug, Copy, Clone)] enum UniformDurationMode { Small { + secs: u64, + nanos: Uniform, + }, + Medium { nanos: Uniform, }, Large { - size: Duration, + min_secs: u64, + min_nanos: u32, + max_secs: u64, + max_nanos: u32, secs: Uniform, } } @@ -737,52 +743,65 @@ impl UniformSampler for UniformDuration { let low = *low_b.borrow(); let high = *high_b.borrow(); assert!(low <= high, "Uniform::new_inclusive called with `low > high`"); - let size = high - low; - let nanos = size - .as_secs() - .checked_mul(1_000_000_000) - .and_then(|n| n.checked_add(size.subsec_nanos() as u64)); - - let mode = match nanos { - Some(nanos) => { - UniformDurationMode::Small { - nanos: Uniform::new_inclusive(0, nanos), - } + + let low_s = low.as_secs(); + let low_n = low.subsec_nanos(); + let high_s = high.as_secs(); + let high_n = high.subsec_nanos(); + + let mode = if low_s == high_s { + UniformDurationMode::Small { + secs: low_s, + nanos: Uniform::new_inclusive(low_n, high_n), } - None => { + } else { + let max = high_s + .checked_mul(1_000_000_000) + .and_then(|n| n.checked_add(high_n as u64)); + + if let Some(higher_bound) = max { + let lower_bound = low_s * 1_000_000_000 + low_n as u64; + UniformDurationMode::Medium { + nanos: Uniform::new_inclusive(lower_bound, higher_bound), + } + } else { UniformDurationMode::Large { - size: size, - secs: Uniform::new_inclusive(0, size.as_secs()), + min_secs: low_s, + min_nanos: low_n, + max_secs: high_s, + max_nanos: high_n, + secs: Uniform::new_inclusive(low_s, high_s), } } }; - UniformDuration { - mode, - offset: low, + mode } } #[inline] fn sample(&self, rng: &mut R) -> Duration { - let d = match self.mode { - UniformDurationMode::Small { nanos } => { + match self.mode { + UniformDurationMode::Small { secs, nanos } => { + let n = nanos.sample(rng); + Duration::new(secs, n) + } + UniformDurationMode::Medium { nanos } => { let nanos = nanos.sample(rng); Duration::new(nanos / 1_000_000_000, (nanos % 1_000_000_000) as u32) } - UniformDurationMode::Large { size, secs } => { + UniformDurationMode::Large { min_secs, min_nanos, max_secs, max_nanos, secs } => { // constant folding means this is at least as fast as `gen_range` let nano_range = Uniform::new(0, 1_000_000_000); loop { - let d = Duration::new(secs.sample(rng), nano_range.sample(rng)); - if d <= size { - break d; + let s = secs.sample(rng); + let n = nano_range.sample(rng); + if !((s == max_secs && n > max_nanos) || (s == min_secs && n < min_nanos)) { + break Duration::new(s, n); } } } - }; - - self.offset + d + } } } From 84ffc8d7469822f18c3aed871da914b3b2c73336 Mon Sep 17 00:00:00 2001 From: Pazzaz Date: Tue, 14 Aug 2018 11:34:10 +0200 Subject: [PATCH 3/6] Improve handling of edge cases in `UniformDuration` --- src/distributions/uniform.rs | 33 +++++++++++++++++++++------------ 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/src/distributions/uniform.rs b/src/distributions/uniform.rs index 87c4322c52a..7eb0ba82725 100644 --- a/src/distributions/uniform.rs +++ b/src/distributions/uniform.rs @@ -694,6 +694,7 @@ uniform_float_impl! { f64x8, u64x8, f64, u64, 64 - 52 } #[derive(Clone, Copy, Debug)] pub struct UniformDuration { mode: UniformDurationMode, + offset: u32, } #[cfg(feature = "std")] @@ -707,8 +708,6 @@ enum UniformDurationMode { nanos: Uniform, }, Large { - min_secs: u64, - min_nanos: u32, max_secs: u64, max_nanos: u32, secs: Uniform, @@ -743,11 +742,19 @@ impl UniformSampler for UniformDuration { let low = *low_b.borrow(); let high = *high_b.borrow(); assert!(low <= high, "Uniform::new_inclusive called with `low > high`"); - + let low_s = low.as_secs(); let low_n = low.subsec_nanos(); - let high_s = high.as_secs(); - let high_n = high.subsec_nanos(); + let mut high_s = high.as_secs(); + let mut high_n = high.subsec_nanos(); + + if high_n < low_n { + high_s = high_s - 1; + high_n = high_n + 1_000_000_000; + } else { + high_s = high_s; + high_n = high_n; + } let mode = if low_s == high_s { UniformDurationMode::Small { @@ -765,17 +772,18 @@ impl UniformSampler for UniformDuration { nanos: Uniform::new_inclusive(lower_bound, higher_bound), } } else { + // An offset is applied to simplify generation of nanoseconds + let max_nanos = high_n - low_n; UniformDurationMode::Large { - min_secs: low_s, - min_nanos: low_n, max_secs: high_s, - max_nanos: high_n, + max_nanos, secs: Uniform::new_inclusive(low_s, high_s), } } }; UniformDuration { - mode + mode, + offset: low_n, } } @@ -790,14 +798,15 @@ impl UniformSampler for UniformDuration { let nanos = nanos.sample(rng); Duration::new(nanos / 1_000_000_000, (nanos % 1_000_000_000) as u32) } - UniformDurationMode::Large { min_secs, min_nanos, max_secs, max_nanos, secs } => { + UniformDurationMode::Large { max_secs, max_nanos, secs } => { // constant folding means this is at least as fast as `gen_range` let nano_range = Uniform::new(0, 1_000_000_000); loop { let s = secs.sample(rng); let n = nano_range.sample(rng); - if !((s == max_secs && n > max_nanos) || (s == min_secs && n < min_nanos)) { - break Duration::new(s, n); + if !(s == max_secs && n > max_nanos) { + let sum = n + self.offset; + break Duration::new(s, sum); } } } From 79450d01dc9d7eb726fdf805a911b68a7414335f Mon Sep 17 00:00:00 2001 From: Pazzaz Date: Tue, 14 Aug 2018 11:43:22 +0200 Subject: [PATCH 4/6] Improve benches for uniform duration --- benches/distributions.rs | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/benches/distributions.rs b/benches/distributions.rs index 6713e7c027a..7d4ab475a6a 100644 --- a/benches/distributions.rs +++ b/benches/distributions.rs @@ -106,10 +106,21 @@ distr_int!(distr_uniform_i128, i128, Uniform::new(-123_456_789_123i128, 123_456_ distr_float!(distr_uniform_f32, f32, Uniform::new(2.26f32, 2.319)); distr_float!(distr_uniform_f64, f64, Uniform::new(2.26f64, 2.319)); -distr_duration!(distr_uniform_duration_largest, Uniform::new(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(u64::max_value()/1000, 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_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(u64::max_value() / 1000, 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(u64::max_value() / 10, 999_999_999), Duration::new((u64::max_value() / 10) + 1, 0)) +); // standard From 9ea83949723c527113f4ea1e43c2378407e5d405 Mon Sep 17 00:00:00 2001 From: Pazzaz Date: Tue, 14 Aug 2018 12:42:20 +0200 Subject: [PATCH 5/6] Remove redundant assignment --- src/distributions/uniform.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/distributions/uniform.rs b/src/distributions/uniform.rs index 7eb0ba82725..0cffbffa628 100644 --- a/src/distributions/uniform.rs +++ b/src/distributions/uniform.rs @@ -751,9 +751,6 @@ impl UniformSampler for UniformDuration { if high_n < low_n { high_s = high_s - 1; high_n = high_n + 1_000_000_000; - } else { - high_s = high_s; - high_n = high_n; } let mode = if low_s == high_s { From 92ff0780a91afc67d7ff63ae73fed42cfac121da Mon Sep 17 00:00:00 2001 From: Pazzaz Date: Tue, 14 Aug 2018 13:07:59 +0200 Subject: [PATCH 6/6] Change uniform duration edge benchmark --- benches/distributions.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/benches/distributions.rs b/benches/distributions.rs index 7d4ab475a6a..1da0f1466c9 100644 --- a/benches/distributions.rs +++ b/benches/distributions.rs @@ -106,11 +106,13 @@ distr_int!(distr_uniform_i128, i128, Uniform::new(-123_456_789_123i128, 123_456_ 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(u64::max_value() / 1000, 1_000_000_000 / 2)) + 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)) @@ -119,7 +121,7 @@ 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(u64::max_value() / 10, 999_999_999), Duration::new((u64::max_value() / 10) + 1, 0)) + Uniform::new_inclusive(Duration::new(LARGE_SEC, 999_999_999), Duration::new(LARGE_SEC + 1, 1)) );