Skip to content

Commit 970720f

Browse files
authored
Merge 92ff078 into 0671cf8
2 parents 0671cf8 + 92ff078 commit 970720f

2 files changed

Lines changed: 92 additions & 27 deletions

File tree

benches/distributions.rs

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ const RAND_BENCH_N: u64 = 1000;
99

1010
use std::mem::size_of;
1111
use test::Bencher;
12+
use std::time::Duration;
1213

1314
use rand::{Rng, FromEntropy};
1415
use rand::rngs::SmallRng;
@@ -54,6 +55,26 @@ macro_rules! distr_float {
5455
}
5556
}
5657

58+
macro_rules! distr_duration {
59+
($fnn:ident, $distr:expr) => {
60+
#[bench]
61+
fn $fnn(b: &mut Bencher) {
62+
let mut rng = SmallRng::from_entropy();
63+
let distr = $distr;
64+
65+
b.iter(|| {
66+
let mut accum = Duration::new(0, 0);
67+
for _ in 0..::RAND_BENCH_N {
68+
let x: Duration = distr.sample(&mut rng);
69+
accum = accum.checked_add(x).unwrap_or(Duration::new(u64::max_value(), 999_999_999));
70+
}
71+
accum
72+
});
73+
b.bytes = size_of::<Duration>() as u64 * ::RAND_BENCH_N;
74+
}
75+
}
76+
}
77+
5778
macro_rules! distr {
5879
($fnn:ident, $ty:ty, $distr:expr) => {
5980
#[bench]
@@ -85,6 +106,25 @@ distr_int!(distr_uniform_i128, i128, Uniform::new(-123_456_789_123i128, 123_456_
85106
distr_float!(distr_uniform_f32, f32, Uniform::new(2.26f32, 2.319));
86107
distr_float!(distr_uniform_f64, f64, Uniform::new(2.26f64, 2.319));
87108

109+
const LARGE_SEC: u64 = u64::max_value() / 1000;
110+
111+
distr_duration!(distr_uniform_duration_largest,
112+
Uniform::new_inclusive(Duration::new(0, 0), Duration::new(u64::max_value(), 999_999_999))
113+
);
114+
distr_duration!(distr_uniform_duration_large,
115+
Uniform::new(Duration::new(0, 0), Duration::new(LARGE_SEC, 1_000_000_000 / 2))
116+
);
117+
distr_duration!(distr_uniform_duration_one,
118+
Uniform::new(Duration::new(0, 0), Duration::new(1, 0))
119+
);
120+
distr_duration!(distr_uniform_duration_variety,
121+
Uniform::new(Duration::new(10000, 423423), Duration::new(200000, 6969954))
122+
);
123+
distr_duration!(distr_uniform_duration_edge,
124+
Uniform::new_inclusive(Duration::new(LARGE_SEC, 999_999_999), Duration::new(LARGE_SEC + 1, 1))
125+
);
126+
127+
88128
// standard
89129
distr_int!(distr_standard_i8, i8, Standard);
90130
distr_int!(distr_standard_i16, i16, Standard);

src/distributions/uniform.rs

Lines changed: 52 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -837,18 +837,23 @@ uniform_float_impl! { f64x8, u64x8, f64, u64, 64 - 52 }
837837
#[cfg(feature = "std")]
838838
#[derive(Clone, Copy, Debug)]
839839
pub struct UniformDuration {
840-
offset: Duration,
841840
mode: UniformDurationMode,
841+
offset: u32,
842842
}
843843

844844
#[cfg(feature = "std")]
845845
#[derive(Debug, Copy, Clone)]
846846
enum UniformDurationMode {
847847
Small {
848+
secs: u64,
849+
nanos: Uniform<u32>,
850+
},
851+
Medium {
848852
nanos: Uniform<u64>,
849853
},
850854
Large {
851-
size: Duration,
855+
max_secs: u64,
856+
max_nanos: u32,
852857
secs: Uniform<u64>,
853858
}
854859
}
@@ -881,52 +886,72 @@ impl UniformSampler for UniformDuration {
881886
let low = *low_b.borrow();
882887
let high = *high_b.borrow();
883888
assert!(low <= high, "Uniform::new_inclusive called with `low > high`");
884-
let size = high - low;
885-
let nanos = size
886-
.as_secs()
887-
.checked_mul(1_000_000_000)
888-
.and_then(|n| n.checked_add(size.subsec_nanos() as u64));
889-
890-
let mode = match nanos {
891-
Some(nanos) => {
892-
UniformDurationMode::Small {
893-
nanos: Uniform::new_inclusive(0, nanos),
894-
}
889+
890+
let low_s = low.as_secs();
891+
let low_n = low.subsec_nanos();
892+
let mut high_s = high.as_secs();
893+
let mut high_n = high.subsec_nanos();
894+
895+
if high_n < low_n {
896+
high_s = high_s - 1;
897+
high_n = high_n + 1_000_000_000;
898+
}
899+
900+
let mode = if low_s == high_s {
901+
UniformDurationMode::Small {
902+
secs: low_s,
903+
nanos: Uniform::new_inclusive(low_n, high_n),
895904
}
896-
None => {
905+
} else {
906+
let max = high_s
907+
.checked_mul(1_000_000_000)
908+
.and_then(|n| n.checked_add(high_n as u64));
909+
910+
if let Some(higher_bound) = max {
911+
let lower_bound = low_s * 1_000_000_000 + low_n as u64;
912+
UniformDurationMode::Medium {
913+
nanos: Uniform::new_inclusive(lower_bound, higher_bound),
914+
}
915+
} else {
916+
// An offset is applied to simplify generation of nanoseconds
917+
let max_nanos = high_n - low_n;
897918
UniformDurationMode::Large {
898-
size: size,
899-
secs: Uniform::new_inclusive(0, size.as_secs()),
919+
max_secs: high_s,
920+
max_nanos,
921+
secs: Uniform::new_inclusive(low_s, high_s),
900922
}
901923
}
902924
};
903-
904925
UniformDuration {
905926
mode,
906-
offset: low,
927+
offset: low_n,
907928
}
908929
}
909930

910931
#[inline]
911932
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Duration {
912-
let d = match self.mode {
913-
UniformDurationMode::Small { nanos } => {
933+
match self.mode {
934+
UniformDurationMode::Small { secs, nanos } => {
935+
let n = nanos.sample(rng);
936+
Duration::new(secs, n)
937+
}
938+
UniformDurationMode::Medium { nanos } => {
914939
let nanos = nanos.sample(rng);
915940
Duration::new(nanos / 1_000_000_000, (nanos % 1_000_000_000) as u32)
916941
}
917-
UniformDurationMode::Large { size, secs } => {
942+
UniformDurationMode::Large { max_secs, max_nanos, secs } => {
918943
// constant folding means this is at least as fast as `gen_range`
919944
let nano_range = Uniform::new(0, 1_000_000_000);
920945
loop {
921-
let d = Duration::new(secs.sample(rng), nano_range.sample(rng));
922-
if d <= size {
923-
break d;
946+
let s = secs.sample(rng);
947+
let n = nano_range.sample(rng);
948+
if !(s == max_secs && n > max_nanos) {
949+
let sum = n + self.offset;
950+
break Duration::new(s, sum);
924951
}
925952
}
926953
}
927-
};
928-
929-
self.offset + d
954+
}
930955
}
931956
}
932957

0 commit comments

Comments
 (0)