Skip to content

Commit 415ef6f

Browse files
committed
Improve performance of isaac64::next_u32.
This does some crazy things with indexing, but is 45% faster. We are no longer throwing away half of the results.
1 parent 707c3e1 commit 415ef6f

File tree

1 file changed

+41
-20
lines changed

1 file changed

+41
-20
lines changed

src/prng/isaac64.rs

+41-20
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,13 @@ impl Isaac64Rng {
137137
/// - We fill `rsl` backwards. The reference implementation reads values
138138
/// from `rsl` in reverse. We read them in the normal direction, to make
139139
/// `fill_bytes` a memcopy. To maintain compatibility we fill in reverse.
140+
/// - We store `index` as if `rsl` contains `u32`'s instead of `u64`'s, plus
141+
/// one. This way we can make more efficient use of the generated results
142+
/// in `next_u32`.
143+
/// For `next_u32` the correct index is `index - 1`.
144+
/// For `next_u64` the correct index is `index >> 1`, which also takes
145+
/// care of any alignment issues that could arise if `next_u64` was called
146+
/// after `next_u32`.
140147
fn isaac64(&mut self) {
141148
self.c += w(1);
142149
// abbreviations
@@ -186,26 +193,48 @@ impl Isaac64Rng {
186193

187194
self.a = a;
188195
self.b = b;
189-
self.index = 0;
196+
self.index = 1;
190197
}
191198
}
192199

193200
impl Rng for Isaac64Rng {
194201
#[inline]
195202
fn next_u32(&mut self) -> u32 {
196-
self.next_u64() as u32
203+
// Using a local variable for `index`, and checking the size avoids a
204+
// bounds check later on.
205+
let mut index = self.index as usize - 1;
206+
if index >= RAND_SIZE * 2 {
207+
self.isaac64();
208+
index = 0;
209+
}
210+
211+
let value;
212+
if cfg!(target_endian = "little") {
213+
// Index as if this is a u32 slice.
214+
let rsl = unsafe { &*(&mut self.rsl as *mut [u64; RAND_SIZE]
215+
as *mut [u32; RAND_SIZE * 2]) };
216+
value = rsl[index];
217+
} else {
218+
// Index into the u64 slice, rotate and truncate the result.
219+
// Works always, also on big-endian systems, but is slower.
220+
let tmp = self.rsl[index >> 1];
221+
value = tmp as u32;
222+
self.rsl[index >> 1] = tmp.rotate_right(32);
223+
}
224+
self.index += 1;
225+
value
197226
}
198227

199228
#[inline]
200229
fn next_u64(&mut self) -> u64 {
201-
let mut index = self.index as usize;
230+
let mut index = self.index as usize >> 1;
202231
if index >= RAND_SIZE {
203232
self.isaac64();
204233
index = 0;
205234
}
206235

207236
let value = self.rsl[index];
208-
self.index += 1;
237+
self.index += 2;
209238
value
210239
}
211240

@@ -217,15 +246,15 @@ impl Rng for Isaac64Rng {
217246
fn fill_bytes(&mut self, dest: &mut [u8]) {
218247
let mut read_len = 0;
219248
while read_len < dest.len() {
220-
if self.index as usize >= RAND_SIZE {
249+
if (self.index as usize >> 1) >= RAND_SIZE {
221250
self.isaac64();
222251
}
223252

224253
let (consumed_u64, filled_u8) =
225-
impls::fill_via_u64_chunks(&mut self.rsl[(self.index as usize)..],
254+
impls::fill_via_u64_chunks(&mut self.rsl[(self.index as usize >> 1)..],
226255
&mut dest[read_len..]);
227256

228-
self.index += consumed_u64 as u32;
257+
self.index += consumed_u64 as u32 * 2;
229258
read_len += filled_u8;
230259
}
231260
}
@@ -385,20 +414,12 @@ mod test {
385414
let mut rng1 = Isaac64Rng::from_seed(seed);
386415
let v = (0..10).map(|_| rng1.next_u32()).collect::<Vec<_>>();
387416
// Subset of above values, as an LE u32 sequence
388-
// TODO: switch to this sequence?
389-
// assert_eq!(v,
390-
// [141028748, 127386717,
391-
// 1058730652, 3347555894,
392-
// 851491469, 4039984500,
393-
// 2692730210, 288449107,
394-
// 646103879, 2782923823]);
395-
// Subset of above values, using only low-half of each u64
396417
assert_eq!(v,
397-
[141028748, 1058730652,
398-
851491469, 2692730210,
399-
646103879, 4195642895,
400-
2836348583, 1312677241,
401-
999139615, 253604626]);
418+
[141028748, 127386717,
419+
1058730652, 3347555894,
420+
851491469, 4039984500,
421+
2692730210, 288449107,
422+
646103879, 2782923823]);
402423
}
403424

404425
#[test]

0 commit comments

Comments
 (0)