Skip to content

Commit d5d9c75

Browse files
authored
Merge pull request #76 from pitdicker/reseeding_perf
Reseeding perf
2 parents 8990da2 + 14f02a2 commit d5d9c75

File tree

2 files changed

+108
-65
lines changed

2 files changed

+108
-65
lines changed

benches/generators.rs

+38
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ use test::{black_box, Bencher};
1111

1212
use rand::{Rng, NewSeeded, Sample, SeedableRng, StdRng, OsRng, JitterRng};
1313
use rand::prng::*;
14+
use rand::reseeding::{ReseedingRng, ReseedWithNew};
1415

1516
macro_rules! gen_bytes {
1617
($fnn:ident, $gen:ident) => {
@@ -102,3 +103,40 @@ fn init_jitter(b: &mut Bencher) {
102103
black_box(JitterRng::new().unwrap());
103104
});
104105
}
106+
107+
108+
109+
#[bench]
110+
fn reseeding_hc128_bytes(b: &mut Bencher) {
111+
let mut rng = ReseedingRng::new(Hc128Rng::new().unwrap(),
112+
128*1024*1024*1024,
113+
ReseedWithNew);
114+
let mut buf = [0u8; BYTES_LEN];
115+
b.iter(|| {
116+
for _ in 0..RAND_BENCH_N {
117+
rng.fill_bytes(&mut buf);
118+
black_box(buf);
119+
}
120+
});
121+
b.bytes = BYTES_LEN as u64 * RAND_BENCH_N;
122+
}
123+
124+
macro_rules! reseeding_uint {
125+
($fnn:ident, $ty:ty) => {
126+
#[bench]
127+
fn $fnn(b: &mut Bencher) {
128+
let mut rng = ReseedingRng::new(Hc128Rng::new().unwrap(),
129+
128*1024*1024*1024,
130+
ReseedWithNew);
131+
b.iter(|| {
132+
for _ in 0..RAND_BENCH_N {
133+
black_box(rng.gen::<$ty>());
134+
}
135+
});
136+
b.bytes = size_of::<$ty>() as u64 * RAND_BENCH_N;
137+
}
138+
}
139+
}
140+
141+
reseeding_uint!(reseeding_hc128_u32, u32);
142+
reseeding_uint!(reseeding_hc128_u64, u64);

src/reseeding.rs

+70-65
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,13 @@
1111
//! A wrapper around another RNG that reseeds it after it
1212
//! generates a certain number of random bytes.
1313
14-
use core::cmp::max;
1514
use {Rng, SeedableRng, Error, ErrorKind};
1615
#[cfg(feature="std")]
1716
use NewSeeded;
1817

1918
/// How many bytes of entropy the underling RNG is allowed to generate
2019
/// before it is reseeded
21-
const DEFAULT_GENERATION_THRESHOLD: u64 = 32 * 1024;
20+
const DEFAULT_RESEEDING_THRESHOLD: i64 = 32 * 1024;
2221

2322
/// A wrapper around any RNG which reseeds the underlying RNG after it
2423
/// has generated a certain number of random bytes.
@@ -32,8 +31,8 @@ const DEFAULT_GENERATION_THRESHOLD: u64 = 32 * 1024;
3231
#[derive(Debug, Clone)]
3332
pub struct ReseedingRng<R, Rsdr: Reseeder<R>> {
3433
rng: R,
35-
generation_threshold: u64,
36-
bytes_generated: u64,
34+
threshold: i64,
35+
bytes_until_reseed: i64,
3736
/// Controls the behaviour when reseeding the RNG.
3837
pub reseeder: Rsdr,
3938
}
@@ -44,116 +43,122 @@ impl<R: Rng, Rsdr: Reseeder<R>> ReseedingRng<R, Rsdr> {
4443
/// # Arguments
4544
///
4645
/// * `rng`: the random number generator to use.
47-
/// * `generation_threshold`: the number of bytes of entropy at which to reseed the RNG.
46+
/// * `threshold`: the number of generated bytes after which to reseed the RNG.
4847
/// * `reseeder`: the reseeding object to use.
49-
pub fn new(rng: R, generation_threshold: u64, reseeder: Rsdr) -> ReseedingRng<R,Rsdr> {
48+
pub fn new(rng: R, threshold: u64, reseeder: Rsdr) -> ReseedingRng<R,Rsdr> {
49+
assert!(threshold <= ::core::i64::MAX as u64);
5050
ReseedingRng {
5151
rng: rng,
52-
generation_threshold: generation_threshold,
53-
bytes_generated: 0,
52+
threshold: threshold as i64,
53+
bytes_until_reseed: threshold as i64,
5454
reseeder: reseeder
5555
}
5656
}
5757

58-
/// Reseed the internal RNG if the number of bytes that have been
59-
/// generated exceed the threshold.
60-
///
58+
/// Reseed the internal RNG.
6159
/// On error, this may delay reseeding or not reseed at all.
62-
pub fn reseed_if_necessary(&mut self) {
63-
if self.bytes_generated >= self.generation_threshold {
64-
let mut err_count = 0;
65-
loop {
66-
if let Err(e) = self.reseeder.reseed(&mut self.rng) {
67-
// TODO: log?
68-
if e.kind.should_wait() {
69-
// Delay reseeding
70-
let delay = max(self.generation_threshold >> 8, self.bytes_generated);
71-
self.bytes_generated -= delay;
72-
break;
73-
} else if e.kind.should_retry() {
74-
if err_count > 4 { // arbitrary limit
75-
// TODO: log details & cause?
76-
break; // give up trying to reseed
77-
}
78-
err_count += 1;
79-
continue; // immediate retry
80-
} else {
81-
break; // give up trying to reseed
60+
#[inline(never)]
61+
pub fn reseed(&mut self) {
62+
self.bytes_until_reseed = self.threshold;
63+
let mut err_count = 0;
64+
loop {
65+
if let Err(e) = self.reseeder.reseed(&mut self.rng) {
66+
// TODO: log?
67+
if e.kind.should_wait() {
68+
// Delay reseeding
69+
self.bytes_until_reseed = self.threshold >> 8;
70+
} else if e.kind.should_retry() {
71+
err_count += 1;
72+
if err_count <= 5 { // arbitrary limit
73+
continue; // retry immediately
8274
}
83-
} else {
84-
break; // no reseeding
8575
}
76+
// give up trying to reseed
8677
}
87-
self.bytes_generated = 0;
78+
break; // successfully reseeded, delayed, or given up.
8879
}
8980
}
81+
9082
/// Reseed the internal RNG if the number of bytes that have been
9183
/// generated exceed the threshold.
9284
///
9385
/// If reseeding fails, return an error with the original cause. Note that
9486
/// if the cause has a permanent failure, we report a transient error and
9587
/// skip reseeding.
96-
pub fn try_reseed_if_necessary(&mut self) -> Result<(), Error> {
97-
if self.bytes_generated >= self.generation_threshold {
98-
if let Err(err) = self.reseeder.reseed(&mut self.rng) {
99-
let newkind = match err.kind {
100-
a @ ErrorKind::NotReady => a,
101-
b @ ErrorKind::Transient => b,
102-
_ => {
103-
self.bytes_generated = 0; // skip reseeding
104-
ErrorKind::Transient
105-
}
106-
};
107-
return Err(Error::with_cause(newkind, "reseeding failed", err));
108-
}
109-
self.bytes_generated = 0;
88+
#[inline(never)]
89+
pub fn try_reseed(&mut self) -> Result<(), Error> {
90+
if let Err(err) = self.reseeder.reseed(&mut self.rng) {
91+
let newkind = match err.kind {
92+
a @ ErrorKind::NotReady => a,
93+
b @ ErrorKind::Transient => b,
94+
_ => {
95+
self.bytes_until_reseed = self.threshold; // skip reseeding
96+
ErrorKind::Transient
97+
}
98+
};
99+
return Err(Error::with_cause(newkind, "reseeding failed", err));
110100
}
101+
self.bytes_until_reseed = self.threshold;
111102
Ok(())
112103
}
113104
}
114105

115106

116107
impl<R: Rng, Rsdr: Reseeder<R>> Rng for ReseedingRng<R, Rsdr> {
117108
fn next_u32(&mut self) -> u32 {
118-
self.reseed_if_necessary();
119-
self.bytes_generated += 4;
120-
self.rng.next_u32()
109+
let value = self.rng.next_u32();
110+
self.bytes_until_reseed -= 4;
111+
if self.bytes_until_reseed <= 0 {
112+
self.reseed();
113+
}
114+
value
121115
}
122116

123117
fn next_u64(&mut self) -> u64 {
124-
self.reseed_if_necessary();
125-
self.bytes_generated += 8;
126-
self.rng.next_u64()
118+
let value = self.rng.next_u64();
119+
self.bytes_until_reseed -= 8;
120+
if self.bytes_until_reseed <= 0 {
121+
self.reseed();
122+
}
123+
value
127124
}
128125

129126
#[cfg(feature = "i128_support")]
130127
fn next_u128(&mut self) -> u128 {
131-
self.reseed_if_necessary();
132-
self.bytes_generated += 16;
133-
self.rng.next_u128()
128+
let value = self.rng.next_u128();
129+
self.bytes_until_reseed -= 16;
130+
if self.bytes_until_reseed <= 0 {
131+
self.reseed();
132+
}
133+
value
134134
}
135135

136136
fn fill_bytes(&mut self, dest: &mut [u8]) {
137-
self.reseed_if_necessary();
138-
self.bytes_generated += dest.len() as u64;
139137
self.rng.fill_bytes(dest);
138+
self.bytes_until_reseed -= dest.len() as i64;
139+
if self.bytes_until_reseed <= 0 {
140+
self.reseed();
141+
}
140142
}
141143

142144
fn try_fill(&mut self, dest: &mut [u8]) -> Result<(), Error> {
143-
self.try_reseed_if_necessary()?;
144-
self.bytes_generated += dest.len() as u64;
145-
self.rng.try_fill(dest)
145+
self.rng.try_fill(dest)?;
146+
self.bytes_until_reseed -= dest.len() as i64;
147+
if self.bytes_until_reseed <= 0 {
148+
self.try_reseed()?;
149+
}
150+
Ok(())
146151
}
147152
}
148153

149154
impl<R: SeedableRng, Rsdr: Reseeder<R>> ReseedingRng<R, Rsdr> {
150155
/// Create a new `ReseedingRng` from the given reseeder and
151-
/// seed. This uses a default value for `generation_threshold`.
156+
/// seed. This uses a default value for `threshold`.
152157
pub fn from_reseeder(rsdr: Rsdr, seed: <R as SeedableRng>::Seed) -> ReseedingRng<R, Rsdr> {
153158
ReseedingRng {
154159
rng: SeedableRng::from_seed(seed),
155-
generation_threshold: DEFAULT_GENERATION_THRESHOLD,
156-
bytes_generated: 0,
160+
threshold: DEFAULT_RESEEDING_THRESHOLD,
161+
bytes_until_reseed: DEFAULT_RESEEDING_THRESHOLD,
157162
reseeder: rsdr
158163
}
159164
}

0 commit comments

Comments
 (0)