Skip to content

Commit 08ecec4

Browse files
committed
Improve performance of ReseedingRng
* Move the check if it is time to reseed out of the `try_reseed_if_necessary` and make sure that function does not get inlined. * Invert the counter direction. This way we can compare against 0 instead of `self.threshold` * Doing the reseed check after generating a value turns out to be a bit faster.`
1 parent 8323fc4 commit 08ecec4

1 file changed

Lines changed: 71 additions & 60 deletions

File tree

src/reseeding.rs

Lines changed: 71 additions & 60 deletions
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,13 +43,14 @@ 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 amount 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
}
@@ -59,101 +59,112 @@ impl<R: Rng, Rsdr: Reseeder<R>> ReseedingRng<R, Rsdr> {
5959
/// generated exceed the threshold.
6060
///
6161
/// 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 {
62+
#[inline(never)]
63+
pub fn reseed(&mut self) {
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+
self.bytes_until_reseed = self.threshold >> 8;
71+
break;
72+
} else if e.kind.should_retry() {
73+
if err_count > 4 { // arbitrary limit
74+
// TODO: log details & cause?
8175
break; // give up trying to reseed
8276
}
77+
err_count += 1;
78+
continue; // immediate retry
8379
} else {
84-
break; // no reseeding
80+
break; // give up trying to reseed
8581
}
82+
} else {
83+
break; // no reseeding
8684
}
87-
self.bytes_generated = 0;
8885
}
86+
self.bytes_until_reseed = self.threshold;
8987
}
9088
/// Reseed the internal RNG if the number of bytes that have been
9189
/// generated exceed the threshold.
9290
///
9391
/// If reseeding fails, return an error with the original cause. Note that
9492
/// if the cause has a permanent failure, we report a transient error and
9593
/// 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;
94+
#[inline(never)]
95+
pub fn try_reseed(&mut self) -> Result<(), Error> {
96+
if let Err(err) = self.reseeder.reseed(&mut self.rng) {
97+
let newkind = match err.kind {
98+
a @ ErrorKind::NotReady => a,
99+
b @ ErrorKind::Transient => b,
100+
_ => {
101+
self.bytes_until_reseed = self.threshold; // skip reseeding
102+
ErrorKind::Transient
103+
}
104+
};
105+
return Err(Error::with_cause(newkind, "reseeding failed", err));
110106
}
107+
self.bytes_until_reseed = self.threshold;
111108
Ok(())
112109
}
113110
}
114111

115112

116113
impl<R: Rng, Rsdr: Reseeder<R>> Rng for ReseedingRng<R, Rsdr> {
117114
fn next_u32(&mut self) -> u32 {
118-
self.reseed_if_necessary();
119-
self.bytes_generated += 4;
120-
self.rng.next_u32()
115+
let value = self.rng.next_u32();
116+
self.bytes_until_reseed -= 4;
117+
if self.bytes_until_reseed <= 0 {
118+
self.reseed();
119+
}
120+
value
121121
}
122122

123123
fn next_u64(&mut self) -> u64 {
124-
self.reseed_if_necessary();
125-
self.bytes_generated += 8;
126-
self.rng.next_u64()
124+
let value = self.rng.next_u64();
125+
self.bytes_until_reseed -= 8;
126+
if self.bytes_until_reseed <= 0 {
127+
self.reseed();
128+
}
129+
value
127130
}
128131

129132
#[cfg(feature = "i128_support")]
130133
fn next_u128(&mut self) -> u128 {
131-
self.reseed_if_necessary();
132-
self.bytes_generated += 16;
133-
self.rng.next_u128()
134+
let value = self.rng.next_u128();
135+
self.bytes_until_reseed -= 16;
136+
if self.bytes_until_reseed <= 0 {
137+
self.reseed();
138+
}
139+
value
134140
}
135141

136142
fn fill_bytes(&mut self, dest: &mut [u8]) {
137-
self.reseed_if_necessary();
138-
self.bytes_generated += dest.len() as u64;
139143
self.rng.fill_bytes(dest);
144+
self.bytes_until_reseed -= dest.len() as i64;
145+
if self.bytes_until_reseed <= 0 {
146+
self.reseed();
147+
}
140148
}
141149

142150
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)
151+
self.rng.try_fill(dest)?;
152+
self.bytes_until_reseed -= dest.len() as i64;
153+
if self.bytes_until_reseed <= 0 {
154+
self.try_reseed()?;
155+
}
156+
Ok(())
146157
}
147158
}
148159

149160
impl<R: SeedableRng, Rsdr: Reseeder<R>> ReseedingRng<R, Rsdr> {
150161
/// Create a new `ReseedingRng` from the given reseeder and
151-
/// seed. This uses a default value for `generation_threshold`.
162+
/// seed. This uses a default value for `threshold`.
152163
pub fn from_reseeder(rsdr: Rsdr, seed: <R as SeedableRng>::Seed) -> ReseedingRng<R, Rsdr> {
153164
ReseedingRng {
154165
rng: SeedableRng::from_seed(seed),
155-
generation_threshold: DEFAULT_GENERATION_THRESHOLD,
156-
bytes_generated: 0,
166+
threshold: DEFAULT_RESEEDING_THRESHOLD,
167+
bytes_until_reseed: DEFAULT_RESEEDING_THRESHOLD,
157168
reseeder: rsdr
158169
}
159170
}

0 commit comments

Comments
 (0)