Skip to content

Commit 7e11084

Browse files
committed
Optimize fill_bytes_via*
1 parent 95ea68c commit 7e11084

File tree

1 file changed

+25
-11
lines changed

1 file changed

+25
-11
lines changed

rand_core/src/impls.rs

Lines changed: 25 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -39,31 +39,45 @@ pub fn next_u64_via_u32<R: RngCore + ?Sized>(rng: &mut R) -> u64 {
3939

4040
macro_rules! fill_bytes_via {
4141
($rng:ident, $next_u:ident, $BYTES:expr, $dest:ident) => {{
42+
// This method is optimized to only call `$next_u()` from one place in
43+
// the loop, which helps to minimize the code size in combination with
44+
// inlining. This has the consequence that if `$dest.len() == 0` it will
45+
// also use a value from the RNG, instead of returning directly. We
46+
// could handle it as a special case, but that could reduce the
47+
// performance of all uses just to improve that of the non-sensical
48+
// zero-length `$dest` scenario a little.
49+
let remainder = $dest.len() % $BYTES;
50+
let len = $dest.len() / $BYTES;
51+
52+
let mut read_len = 0;
4253
let mut left = $dest;
43-
while left.len() >= $BYTES {
44-
let (l, r) = {left}.split_at_mut($BYTES);
45-
left = r;
46-
let chunk: [u8; $BYTES] = unsafe {
54+
let mut chunk: [u8; $BYTES];
55+
loop {
56+
chunk = unsafe {
4757
transmute($rng.$next_u().to_le())
4858
};
49-
l.copy_from_slice(&chunk);
59+
if read_len < len {
60+
let (l, r) = {left}.split_at_mut($BYTES);
61+
left = r;
62+
l.copy_from_slice(&chunk);
63+
read_len += $BYTES;
64+
}
65+
if read_len == len { break; }
5066
}
51-
let n = left.len();
52-
if n > 0 {
53-
let chunk: [u8; $BYTES] = unsafe {
54-
transmute($rng.$next_u().to_le())
55-
};
56-
left.copy_from_slice(&chunk[..n]);
67+
if remainder > 0 {
68+
left.copy_from_slice(&chunk[..remainder]);
5769
}
5870
}}
5971
}
6072

6173
/// Implement `fill_bytes` via `next_u32`, little-endian order.
74+
#[inline(always)]
6275
pub fn fill_bytes_via_u32<R: RngCore + ?Sized>(rng: &mut R, dest: &mut [u8]) {
6376
fill_bytes_via!(rng, next_u32, 4, dest)
6477
}
6578

6679
/// Implement `fill_bytes` via `next_u64`, little-endian order.
80+
#[inline(always)]
6781
pub fn fill_bytes_via_u64<R: RngCore + ?Sized>(rng: &mut R, dest: &mut [u8]) {
6882
fill_bytes_via!(rng, next_u64, 8, dest)
6983
}

0 commit comments

Comments
 (0)