Skip to content

Commit

Permalink
Add benchmarks for u32/u64 functions.
Browse files Browse the repository at this point in the history
This allows us to see the effect of any specicalized implementations for
`getrandom::u32` or `getrandom::u64`. As expected, on Linux (which just
uses the default implementation in `utils.rs`) there is no change:
```
test bench_u32                 ... bench:         196.50 ns/iter (+/- 4.85) = 20 MB/s
test bench_u32_via_fill        ... bench:         198.25 ns/iter (+/- 1.78) = 20 MB/s
test bench_u64                 ... bench:         196.95 ns/iter (+/- 2.99) = 40 MB/s
test bench_u64_via_fill        ... bench:         197.62 ns/iter (+/- 2.24) = 40 MB/s
```
but when using the `rdrand` backend (which is specialized), there is a
mesurable difference.
```
test bench_u32                 ... bench:          16.84 ns/iter (+/- 0.09) = 250 MB/s
test bench_u32_via_fill        ... bench:          18.40 ns/iter (+/- 0.28) = 222 MB/s
test bench_u64                 ... bench:          16.62 ns/iter (+/- 0.06) = 500 MB/s
test bench_u64_via_fill        ... bench:          17.70 ns/iter (+/- 0.08) = 470 MB/s
```

Signed-off-by: Joe Richey <[email protected]>
  • Loading branch information
josephlr committed Jan 10, 2025
1 parent 9fb4a9a commit 8e93912
Showing 1 changed file with 51 additions and 1 deletion.
52 changes: 51 additions & 1 deletion benches/buffer.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
#![feature(test, maybe_uninit_uninit_array_transpose)]
extern crate test;

use std::mem::MaybeUninit;
use std::{
mem::{size_of, MaybeUninit},
slice,
};

// Call getrandom on a zero-initialized stack buffer
#[inline(always)]
Expand All @@ -19,6 +22,53 @@ fn bench_fill_uninit<const N: usize>() {
test::black_box(buf);
}

#[bench]
pub fn bench_u32(b: &mut test::Bencher) {
#[inline(never)]
fn inner() -> u32 {
getrandom::u32().unwrap()
}
b.bytes = 4;
b.iter(inner);
}
#[bench]
pub fn bench_u32_via_fill(b: &mut test::Bencher) {
#[inline(never)]
fn inner() -> u32 {
let mut res = MaybeUninit::<u32>::uninit();
let dst: &mut [MaybeUninit<u8>] =
unsafe { slice::from_raw_parts_mut(res.as_mut_ptr().cast(), size_of::<u32>()) };
getrandom::fill_uninit(dst).unwrap();
unsafe { res.assume_init() }
}
b.bytes = 4;
b.iter(inner);
}

#[bench]
pub fn bench_u64(b: &mut test::Bencher) {
#[inline(never)]
fn inner() -> u64 {
getrandom::u64().unwrap()
}
b.bytes = 8;
b.iter(inner);
}

#[bench]
pub fn bench_u64_via_fill(b: &mut test::Bencher) {
#[inline(never)]
fn inner() -> u64 {
let mut res = MaybeUninit::<u64>::uninit();
let dst: &mut [MaybeUninit<u8>] =
unsafe { slice::from_raw_parts_mut(res.as_mut_ptr().cast(), size_of::<u64>()) };
getrandom::fill_uninit(dst).unwrap();
unsafe { res.assume_init() }
}
b.bytes = 8;
b.iter(inner);
}

// We benchmark using #[inline(never)] "inner" functions for two reasons:
// - Avoiding inlining reduces a source of variance when running benchmarks.
// - It is _much_ easier to get the assembly or IR for the inner loop.
Expand Down

0 comments on commit 8e93912

Please sign in to comment.