Skip to content

Commit aa96363

Browse files
authored
Add benchmarks for u32/u64 functions. (#582)
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 ```
1 parent e38855c commit aa96363

File tree

1 file changed

+51
-1
lines changed

1 file changed

+51
-1
lines changed

benches/buffer.rs

+51-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
#![feature(test, maybe_uninit_uninit_array_transpose)]
22
extern crate test;
33

4-
use std::mem::MaybeUninit;
4+
use std::{
5+
mem::{size_of, MaybeUninit},
6+
slice,
7+
};
58

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

25+
#[bench]
26+
pub fn bench_u32(b: &mut test::Bencher) {
27+
#[inline(never)]
28+
fn inner() -> u32 {
29+
getrandom::u32().unwrap()
30+
}
31+
b.bytes = 4;
32+
b.iter(inner);
33+
}
34+
#[bench]
35+
pub fn bench_u32_via_fill(b: &mut test::Bencher) {
36+
#[inline(never)]
37+
fn inner() -> u32 {
38+
let mut res = MaybeUninit::<u32>::uninit();
39+
let dst: &mut [MaybeUninit<u8>] =
40+
unsafe { slice::from_raw_parts_mut(res.as_mut_ptr().cast(), size_of::<u32>()) };
41+
getrandom::fill_uninit(dst).unwrap();
42+
unsafe { res.assume_init() }
43+
}
44+
b.bytes = 4;
45+
b.iter(inner);
46+
}
47+
48+
#[bench]
49+
pub fn bench_u64(b: &mut test::Bencher) {
50+
#[inline(never)]
51+
fn inner() -> u64 {
52+
getrandom::u64().unwrap()
53+
}
54+
b.bytes = 8;
55+
b.iter(inner);
56+
}
57+
58+
#[bench]
59+
pub fn bench_u64_via_fill(b: &mut test::Bencher) {
60+
#[inline(never)]
61+
fn inner() -> u64 {
62+
let mut res = MaybeUninit::<u64>::uninit();
63+
let dst: &mut [MaybeUninit<u8>] =
64+
unsafe { slice::from_raw_parts_mut(res.as_mut_ptr().cast(), size_of::<u64>()) };
65+
getrandom::fill_uninit(dst).unwrap();
66+
unsafe { res.assume_init() }
67+
}
68+
b.bytes = 8;
69+
b.iter(inner);
70+
}
71+
2272
// We benchmark using #[inline(never)] "inner" functions for two reasons:
2373
// - Avoiding inlining reduces a source of variance when running benchmarks.
2474
// - It is _much_ easier to get the assembly or IR for the inner loop.

0 commit comments

Comments
 (0)