#![cfg(feature = "alloc-global")]
#![allow(
clippy::cast_possible_truncation,
clippy::needless_pass_by_value,
clippy::semicolon_if_nothing_returned
)]
use std::alloc::{GlobalAlloc, Layout};
use std::hint::black_box;
use std::time::Duration;
use criterion::{criterion_group, criterion_main, Criterion};
use sefer_alloc::SeferAlloc;
#[inline]
fn alloc_touch<A: GlobalAlloc>(a: &A, size: usize) -> *mut u8 {
let layout =
Layout::from_size_align(size, 8).unwrap_or_else(|_| Layout::from_size_align(8, 8).unwrap());
let ptr = unsafe { a.alloc(layout) };
if !ptr.is_null() {
unsafe {
ptr.write(0xAB);
ptr.add(size - 1).write(0xCD);
}
}
ptr
}
#[inline]
fn free_touched<A: GlobalAlloc>(a: &A, ptr: *mut u8, size: usize) {
if ptr.is_null() {
return;
}
let layout =
Layout::from_size_align(size, 8).unwrap_or_else(|_| Layout::from_size_align(8, 8).unwrap());
unsafe { a.dealloc(ptr, layout) };
}
#[inline]
fn grow<A: GlobalAlloc>(a: &A, ptr: *mut u8, old_size: usize, new_size: usize) -> *mut u8 {
if ptr.is_null() || old_size == 0 {
return alloc_touch(a, new_size);
}
let old_layout = Layout::from_size_align(old_size, 8)
.unwrap_or_else(|_| Layout::from_size_align(8, 8).unwrap());
let new_ptr = unsafe { a.realloc(ptr, old_layout, new_size) };
if new_ptr.is_null() {
unsafe { a.dealloc(ptr, old_layout) };
}
new_ptr
}
const SMALL_SIZES: &[usize] = &[64, 128, 256, 512, 1024];
const SMALL_REPS: usize = 8;
const GROW_START: usize = 64;
const GROW_STEPS: usize = 10;
const MEDIUM_SIZES: &[usize] = &[2048, 4096, 8192, 16_384];
const MEDIUM_REPS: usize = 4;
fn pipeline_iteration<A: GlobalAlloc>(a: &A) {
let total_small = SMALL_SIZES.len() * SMALL_REPS;
let mut small_ptrs: [*mut u8; 40] = [core::ptr::null_mut(); 40]; let mut small_sizes: [usize; 40] = [0usize; 40];
let mut si = 0usize;
for rep in 0..SMALL_REPS {
for &sz in SMALL_SIZES {
let p = alloc_touch(a, sz);
if si < total_small {
small_ptrs[si] = p;
small_sizes[si] = sz;
si += 1;
}
black_box(rep);
}
}
let mut grow_size = GROW_START;
let mut grow_ptr: *mut u8 = alloc_touch(a, grow_size);
for _ in 0..GROW_STEPS {
let new_size = (grow_size * 3 / 2).max(grow_size + 8);
grow_ptr = grow(a, grow_ptr, grow_size, new_size);
grow_size = new_size;
}
black_box(grow_ptr);
let total_medium = MEDIUM_SIZES.len() * MEDIUM_REPS;
let mut med_ptrs: [*mut u8; 16] = [core::ptr::null_mut(); 16]; let mut med_sizes: [usize; 16] = [0usize; 16];
let mut mi = 0usize;
for rep in 0..MEDIUM_REPS {
for &sz in MEDIUM_SIZES {
let p = alloc_touch(a, sz);
if mi < total_medium {
med_ptrs[mi] = p;
med_sizes[mi] = sz;
mi += 1;
}
black_box(rep);
}
}
for i in 0..si {
free_touched(a, small_ptrs[i], small_sizes[i]);
}
if !grow_ptr.is_null() {
free_touched(a, grow_ptr, grow_size);
}
for i in 0..mi {
free_touched(a, med_ptrs[i], med_sizes[i]);
}
}
fn bench_async_pattern(c: &mut Criterion) {
let mut group = c.benchmark_group("heap_async_pattern");
group.sample_size(10);
group.warm_up_time(Duration::from_secs(1));
group.measurement_time(Duration::from_secs(3));
let sefer = SeferAlloc::new();
pipeline_iteration(&sefer);
group.bench_function("SeferAlloc/pipeline", |b| {
b.iter(|| pipeline_iteration(&sefer))
});
group.finish();
}
criterion_group!(benches, bench_async_pattern);
criterion_main!(benches);