Skip to content

Commit

Permalink
Rework benchmarks
Browse files Browse the repository at this point in the history
  • Loading branch information
josephlr committed Oct 17, 2022
1 parent 033fc76 commit 4719b5f
Showing 1 changed file with 32 additions and 82 deletions.
114 changes: 32 additions & 82 deletions benches/mod.rs
Original file line number Diff line number Diff line change
@@ -1,106 +1,67 @@
#![feature(test)]
extern crate test;

use std::{
alloc::{alloc_zeroed, dealloc, Layout},
mem::{self, MaybeUninit},
ptr::NonNull,
};
use core::mem::MaybeUninit;

// AlignedBuffer is like a Box<[u8; N]> except that it is always N-byte aligned
struct AlignedBuffer<const N: usize>(NonNull<[u8; N]>);

impl<const N: usize> AlignedBuffer<N> {
fn layout() -> Layout {
Layout::from_size_align(N, N).unwrap()
}

fn new() -> Self {
let p = unsafe { alloc_zeroed(Self::layout()) } as *mut [u8; N];
Self(NonNull::new(p).unwrap())
}

fn buf(&mut self) -> &mut [u8; N] {
unsafe { self.0.as_mut() }
}
}

impl<const N: usize> Drop for AlignedBuffer<N> {
fn drop(&mut self) {
unsafe { dealloc(self.0.as_ptr() as *mut u8, Self::layout()) }
}
}

// Used to benchmark the throughput of getrandom in an optimal scenario.
// The buffer is hot, and does not require initialization.
// Used to benchmark the throughput of getrandom where we have to initialize the
// buffer every time.
#[inline(always)]
fn bench<const N: usize>(b: &mut test::Bencher) {
let mut ab = AlignedBuffer::<N>::new();
let buf = ab.buf();
b.iter(|| {
getrandom::getrandom(&mut buf[..]).unwrap();
test::black_box(&buf);
});
b.bytes = N as u64;
}

// Used to benchmark the throughput of getrandom is a slightly less optimal
// scenario. The buffer is still hot, but requires initialization.
#[inline(always)]
fn bench_with_init<const N: usize>(b: &mut test::Bencher) {
let mut ab = AlignedBuffer::<N>::new();
let buf = ab.buf();
b.iter(|| {
for byte in buf.iter_mut() {
*byte = 0;
}
getrandom::getrandom(&mut buf[..]).unwrap();
let mut buf = [0u8; N];
getrandom::getrandom(&mut buf).unwrap();
test::black_box(&buf);
});
b.bytes = N as u64;
}

// Used to benchmark the benefit of `getrandom_uninit` compared to
// zero-initializing a buffer and then using `getrandom` (`bench_with_init`
// above).
// Used to benchmark getrandom_uninit, where we don't need to initilize the
// buffer each time.
#[inline(always)]
fn bench_uninit<const N: usize>(b: &mut test::Bencher) {
let mut ab = AlignedBuffer::<N>::new();
let buf = ab.buf();
// SAFETY: `buf` doesn't escape this scope.
let buf = unsafe { slice_as_uninit_mut(buf) };
b.bytes = N as u64;
b.iter(|| {
let _ = getrandom::getrandom_uninit_slice(buf);
})
let mut buf = [MaybeUninit::<u8>::uninit(); N];
let buf: &[u8] = getrandom::getrandom_uninit_slice(&mut buf).unwrap();
test::black_box(buf);
});
}

// 32 bytes (256-bit) is the seed sized used for rand::thread_rng
const SEED: usize = 32;
// Common size of a page, 4 KiB
const PAGE: usize = 4096;
// Large buffer to get asymptotic performance, 2 MiB
const LARGE: usize = 1 << 21;

#[bench]
fn bench_seed(b: &mut test::Bencher) {
bench::<SEED>(b);
fn bench_16(b: &mut test::Bencher) {
bench::<16>(b);
}
#[bench]
fn bench_seed_init(b: &mut test::Bencher) {
bench_with_init::<SEED>(b);
fn bench_16_uninit(b: &mut test::Bencher) {
bench_uninit::<16>(b);
}

#[bench]
fn bench_32(b: &mut test::Bencher) {
bench::<32>(b);
}
#[bench]
fn bench_seed_uninit(b: &mut test::Bencher) {
bench_uninit::<SEED>(b);
fn bench_32_uninit(b: &mut test::Bencher) {
bench_uninit::<32>(b);
}

#[bench]
fn bench_page(b: &mut test::Bencher) {
bench::<PAGE>(b);
fn bench_256(b: &mut test::Bencher) {
bench::<256>(b);
}
#[bench]
fn bench_page_init(b: &mut test::Bencher) {
bench_with_init::<PAGE>(b);
fn bench_256_uninit(b: &mut test::Bencher) {
bench_uninit::<256>(b);
}

#[bench]
fn bench_page(b: &mut test::Bencher) {
bench::<PAGE>(b);
}
#[bench]
fn bench_page_uninit(b: &mut test::Bencher) {
Expand All @@ -112,17 +73,6 @@ fn bench_large(b: &mut test::Bencher) {
bench::<LARGE>(b);
}
#[bench]
fn bench_large_init(b: &mut test::Bencher) {
bench_with_init::<LARGE>(b);
}
#[bench]
fn bench_large_uninit(b: &mut test::Bencher) {
bench_uninit::<LARGE>(b);
}

// TODO: Safety note.
#[inline(always)]
unsafe fn slice_as_uninit_mut<T>(slice: &mut [T]) -> &mut [MaybeUninit<T>] {
// SAFETY: `MaybeUninit<T>` is guaranteed to be layout-compatible with `T`.
mem::transmute(slice)
}

0 comments on commit 4719b5f

Please sign in to comment.