|
1 | 1 | #![feature(test)] |
2 | | -extern crate test; |
3 | | - |
4 | | -use std::{ |
5 | | - alloc::{alloc_zeroed, dealloc, Layout}, |
6 | | - ptr::NonNull, |
7 | | -}; |
8 | | - |
9 | | -// AlignedBuffer is like a Box<[u8; N]> except that it is always N-byte aligned |
10 | | -struct AlignedBuffer<const N: usize>(NonNull<[u8; N]>); |
| 2 | +#![feature(maybe_uninit_as_bytes)] |
11 | 3 |
|
12 | | -impl<const N: usize> AlignedBuffer<N> { |
13 | | - fn layout() -> Layout { |
14 | | - Layout::from_size_align(N, N).unwrap() |
15 | | - } |
16 | | - |
17 | | - fn new() -> Self { |
18 | | - let p = unsafe { alloc_zeroed(Self::layout()) } as *mut [u8; N]; |
19 | | - Self(NonNull::new(p).unwrap()) |
20 | | - } |
21 | | - |
22 | | - fn buf(&mut self) -> &mut [u8; N] { |
23 | | - unsafe { self.0.as_mut() } |
24 | | - } |
25 | | -} |
| 4 | +extern crate test; |
26 | 5 |
|
27 | | -impl<const N: usize> Drop for AlignedBuffer<N> { |
28 | | - fn drop(&mut self) { |
29 | | - unsafe { dealloc(self.0.as_ptr() as *mut u8, Self::layout()) } |
30 | | - } |
31 | | -} |
| 6 | +use std::mem::MaybeUninit; |
32 | 7 |
|
33 | 8 | // Used to benchmark the throughput of getrandom in an optimal scenario. |
34 | 9 | // The buffer is hot, and does not require initialization. |
35 | 10 | #[inline(always)] |
36 | | -fn bench<const N: usize>(b: &mut test::Bencher) { |
37 | | - let mut ab = AlignedBuffer::<N>::new(); |
38 | | - let buf = ab.buf(); |
| 11 | +fn bench_getrandom<const N: usize>(b: &mut test::Bencher) { |
| 12 | + b.bytes = N as u64; |
39 | 13 | b.iter(|| { |
| 14 | + let mut buf = [0u8; N]; |
40 | 15 | getrandom::getrandom(&mut buf[..]).unwrap(); |
41 | | - test::black_box(&buf); |
| 16 | + test::black_box(buf); |
42 | 17 | }); |
43 | | - b.bytes = N as u64; |
44 | 18 | } |
45 | 19 |
|
46 | 20 | // Used to benchmark the throughput of getrandom is a slightly less optimal |
47 | 21 | // scenario. The buffer is still hot, but requires initialization. |
48 | 22 | #[inline(always)] |
49 | | -fn bench_with_init<const N: usize>(b: &mut test::Bencher) { |
50 | | - let mut ab = AlignedBuffer::<N>::new(); |
51 | | - let buf = ab.buf(); |
| 23 | +fn bench_getrandom_uninit<const N: usize>(b: &mut test::Bencher) { |
| 24 | + b.bytes = N as u64; |
52 | 25 | b.iter(|| { |
53 | | - for byte in buf.iter_mut() { |
54 | | - *byte = 0; |
55 | | - } |
56 | | - getrandom::getrandom(&mut buf[..]).unwrap(); |
57 | | - test::black_box(&buf); |
| 26 | + let mut buf: MaybeUninit<[u8; N]> = MaybeUninit::uninit(); |
| 27 | + let _ = getrandom::getrandom_uninit(buf.as_bytes_mut()).unwrap(); |
| 28 | + let buf: [u8; N] = unsafe { buf.assume_init() }; |
| 29 | + test::black_box(buf) |
58 | 30 | }); |
59 | | - b.bytes = N as u64; |
60 | 31 | } |
61 | 32 |
|
62 | | -// 32 bytes (256-bit) is the seed sized used for rand::thread_rng |
63 | | -const SEED: usize = 32; |
64 | | -// Common size of a page, 4 KiB |
65 | | -const PAGE: usize = 4096; |
66 | | -// Large buffer to get asymptotic performance, 2 MiB |
67 | | -const LARGE: usize = 1 << 21; |
| 33 | +macro_rules! bench { |
| 34 | + ( $name:ident, $size:expr ) => { |
| 35 | + pub mod $name { |
| 36 | + #[bench] |
| 37 | + pub fn bench_getrandom(b: &mut test::Bencher) { |
| 38 | + super::bench_getrandom::<{ $size }>(b); |
| 39 | + } |
68 | 40 |
|
69 | | -#[bench] |
70 | | -fn bench_seed(b: &mut test::Bencher) { |
71 | | - bench::<SEED>(b); |
72 | | -} |
73 | | -#[bench] |
74 | | -fn bench_seed_init(b: &mut test::Bencher) { |
75 | | - bench_with_init::<SEED>(b); |
| 41 | + #[bench] |
| 42 | + pub fn bench_getrandom_uninit(b: &mut test::Bencher) { |
| 43 | + super::bench_getrandom_uninit::<{ $size }>(b); |
| 44 | + } |
| 45 | + } |
| 46 | + }; |
76 | 47 | } |
77 | 48 |
|
78 | | -#[bench] |
79 | | -fn bench_page(b: &mut test::Bencher) { |
80 | | - bench::<PAGE>(b); |
81 | | -} |
82 | | -#[bench] |
83 | | -fn bench_page_init(b: &mut test::Bencher) { |
84 | | - bench_with_init::<PAGE>(b); |
85 | | -} |
| 49 | +// 16 bytes (128 bits) is the size of an 128-bit AES key/nonce. |
| 50 | +bench!(aes128, 128 / 8); |
86 | 51 |
|
87 | | -#[bench] |
88 | | -fn bench_large(b: &mut test::Bencher) { |
89 | | - bench::<LARGE>(b); |
90 | | -} |
91 | | -#[bench] |
92 | | -fn bench_large_init(b: &mut test::Bencher) { |
93 | | - bench_with_init::<LARGE>(b); |
94 | | -} |
| 52 | +// 32 bytes (256 bits) is the seed sized used for rand::thread_rng |
| 53 | +// and the `random` value in a ClientHello/ServerHello for TLS. |
| 54 | +// This is also the size of a 256-bit AES/HMAC/P-256/Curve25519 key |
| 55 | +// and/or nonce. |
| 56 | +bench!(p256, 256 / 8); |
| 57 | + |
| 58 | +// A P-384/HMAC-384 key and/or nonce. |
| 59 | +bench!(p384, 384 / 8); |
| 60 | + |
| 61 | +// Initializing larger buffers is not the primary use case of this library, as |
| 62 | +// this should normally be done by a userspace CSPRNG. However, we have a test |
| 63 | +// here to see the effects of a lower (amortized) syscall overhead. |
| 64 | +bench!(page, 4096); |
0 commit comments