I implemented a simple function that generated random bytes Vec<u8>
with given n
size. Then I wondered how can I optimize this.
Here is the first implementation:
use rand::{Rng, RngCore, SeedableRng}; use rand_chacha::ChaCha20Rng; fn random_bytes(n: usize) -> Vec<u8> { let mut bytes = Vec::with_capacity(n); let mut rng = ChaCha20Rng::from_entropy(); for _ in 0..n { let b = rng.gen::<u8>(); bytes.push(b); } bytes }
I wanted to go through unsafe path to make things interesting. First thing that I came up with is to make allocated memory uninitialized. Using Box::<[T]>::new_uninit_slice
which happens to be stabilized in 1.82.0.
Second thing that came to my mind is to fill the bytes in chunks. RngCore
trait provides next_u64
method which returns a random u64
. But I could not figure out how to handle remainders. (e.g. 13 bytes is not multiple of 8 bytes leaving 5 byte chunk at the end). So I relied on using RngCore::fill_bytes
method.
Here is the unsafe implementation:
fn unsafe_random_bytes(n: usize) -> Vec<u8> { let mut uninit_boxed = Box::<[u8]>::new_uninit_slice(n); let bytes = { let ptr = uninit_boxed[0].as_mut_ptr(); unsafe { std::slice::from_raw_parts_mut(ptr, n) } }; let mut rng = ChaCha20Rng::from_entropy(); rng.fill_bytes(bytes); unsafe { uninit_boxed.assume_init().into_vec() } }
There are two unsafe blocks, first is for creating mutable slice from pointer and second is the assuming the memory initialized. I'm not sure if there is any UBs. But it seems to be working. In my opinion, since box ensures that n
amount of type T
is allocated. We can construct a mutable slice from first element address, since MaybeUninit
and ManuallyDrop
is #[repr(transparent)]
.
fill_bytes
from rand::RngCore
guarantees that the destination is filled with new data. Thus, assuming initialized for the second unsafe block also ok.
Here is the comparison for both functions using criterion
crate:
Let me know what your thoughts are. I may be missing something or don't have the knowledge.
from_raw_parts_mut
requires that all elements are initialized.\$\endgroup\$