Skip to content

Add/Rework benchmarks to track initialization cost #272

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jul 13, 2022
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
88 changes: 80 additions & 8 deletions benches/mod.rs
Original file line number Diff line number Diff line change
@@ -1,22 +1,94 @@
#![feature(test)]
extern crate test;

#[bench]
fn bench_64(b: &mut test::Bencher) {
let mut buf = [0u8; 64];
use std::{
alloc::{alloc_zeroed, dealloc, Layout},
ptr::NonNull,
};

// 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.
#[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 = buf.len() as u64;
b.bytes = N as u64;
}

#[bench]
fn bench_65536(b: &mut test::Bencher) {
let mut buf = [0u8; 65536];
// 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();
test::black_box(&buf);
});
b.bytes = buf.len() as u64;
b.bytes = N as u64;
}

// 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);
}
#[bench]
fn bench_seed_init(b: &mut test::Bencher) {
bench_with_init::<SEED>(b);
}

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

#[bench]
fn bench_large(b: &mut test::Bencher) {
bench::<LARGE>(b);
}
#[bench]
fn bench_large_init(b: &mut test::Bencher) {
bench_with_init::<LARGE>(b);
}