|
| 1 | +use std::alloc::{GlobalAlloc, Layout, System}; |
| 2 | + |
1 | 3 | pub use criterion::*;
|
| 4 | + |
| 5 | +#[global_allocator] |
| 6 | +static GLOBAL: NeverGrowInPlaceAllocator = NeverGrowInPlaceAllocator; |
| 7 | + |
| 8 | +/// Global allocator for use in benchmarks. |
| 9 | +/// |
| 10 | +/// A thin wrapper around Rust's default [`System`] allocator. It passes through `alloc` |
| 11 | +/// and `dealloc` methods to [`System`], but does not implement [`GlobalAlloc::realloc`]. |
| 12 | +/// |
| 13 | +/// Rationale for this is: |
| 14 | +/// |
| 15 | +/// `realloc` for default [`System`] allocator calls `libc::realloc`, which may either: |
| 16 | +/// 1. allow the allocation to grow in place. or |
| 17 | +/// 2. create a new allocation, and copy memory from old allocation to the new one. |
| 18 | +/// |
| 19 | +/// Whether allocations can grow in place or not depends on the state of the operating system's |
| 20 | +/// memory tables, and so is inherently non-deterministic. Using default `System` allocator |
| 21 | +/// therefore produces large and unpredictable variance in benchmarks. |
| 22 | +/// |
| 23 | +/// By not providing a `realloc` method, this custom allocator delegates to the default |
| 24 | +/// [`GlobalAlloc::realloc`] implementation which *never* grows in place. |
| 25 | +/// It therefore represents the "worse case scenario" for memory allocation performance. |
| 26 | +/// This behavior is consistent and predictable, and therefore stabilizes benchmark results. |
| 27 | +pub struct NeverGrowInPlaceAllocator; |
| 28 | + |
| 29 | +/// SAFETY: Methods simply delegate to `System` allocator |
| 30 | +#[allow(unsafe_code, clippy::undocumented_unsafe_blocks)] |
| 31 | +unsafe impl GlobalAlloc for NeverGrowInPlaceAllocator { |
| 32 | + unsafe fn alloc(&self, layout: Layout) -> *mut u8 { |
| 33 | + System.alloc(layout) |
| 34 | + } |
| 35 | + |
| 36 | + unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { |
| 37 | + System.dealloc(ptr, layout); |
| 38 | + } |
| 39 | +} |
0 commit comments