#![cfg(all(feature = "alloc-global", feature = "alloc-xthread"))]
#![allow(
clippy::cast_possible_truncation,
clippy::cast_precision_loss,
clippy::semicolon_if_nothing_returned
)]
use std::alloc::{GlobalAlloc, Layout, System};
use std::hint::black_box;
use std::sync::mpsc::{channel, Receiver, Sender};
use std::sync::{Arc, Barrier};
use std::thread;
use std::time::Instant;
use sefer_alloc::SeferMalloc;
struct Block {
ptr: *mut u8,
layout: Layout,
}
unsafe impl Send for Block {}
struct XorShift64(u64);
impl XorShift64 {
fn new(seed: u64) -> Self {
Self(seed | 1)
}
#[inline]
fn next_u64(&mut self) -> u64 {
let mut x = self.0;
x ^= x >> 12;
x ^= x << 25;
x ^= x >> 27;
self.0 = x;
x.wrapping_mul(0x2545_F491_4F6C_DD1D)
}
#[inline]
fn below(&mut self, n: usize) -> usize {
(self.next_u64() % n as u64) as usize
}
}
#[inline]
fn pick_size(rng: &mut XorShift64) -> usize {
let r = rng.next_u64();
if r.is_multiple_of(32) {
512 + (r >> 8) as usize % (8 * 1024 - 512)
} else {
16 + (r >> 8) as usize % (512 - 16)
}
}
#[inline]
fn layout_for(size: usize) -> Layout {
Layout::from_size_align(size.max(1), 8).unwrap()
}
#[inline]
unsafe fn alloc_block<A: GlobalAlloc>(a: &A, rng: &mut XorShift64) -> Block {
let layout = layout_for(pick_size(rng));
let ptr = unsafe { a.alloc(layout) };
if !ptr.is_null() {
unsafe { ptr.write(0xA5) };
}
Block { ptr, layout }
}
#[inline]
unsafe fn free_block<A: GlobalAlloc>(a: &A, block: Block) {
if !block.ptr.is_null() {
unsafe { a.dealloc(block.ptr, block.layout) };
}
}
#[inline]
unsafe fn drain_mailbox<A: GlobalAlloc>(a: &A, rx: &Receiver<Block>, count: &mut u64) {
while let Ok(block) = rx.try_recv() {
unsafe { free_block(a, block) };
*count += 1;
}
}
unsafe fn larson_worker<A: GlobalAlloc>(
a: &A,
seed: u64,
steps: usize,
working_set: usize,
senders: &[Sender<Block>],
rx: &Receiver<Block>,
self_idx: usize,
) -> u64 {
let mut rng = XorShift64::new(seed);
let mut ops: u64 = 0;
let mut slots: Vec<Option<Block>> = Vec::with_capacity(working_set);
for _ in 0..working_set {
slots.push(Some(unsafe { alloc_block(a, &mut rng) }));
}
const HANDOFF_EVERY: usize = 16;
let n_threads = senders.len();
for step in 0..steps {
unsafe { drain_mailbox(a, rx, &mut ops) };
let idx = rng.below(working_set);
if n_threads > 1 && step % HANDOFF_EVERY == 0 {
if let Some(block) = slots[idx].take() {
let mut target = rng.below(n_threads);
if target == self_idx {
target = (target + 1) % n_threads;
}
if senders[target].send(block).is_err() {
}
}
} else if let Some(block) = slots[idx].take() {
unsafe { free_block(a, block) };
}
slots[idx] = Some(unsafe { alloc_block(a, &mut rng) });
ops += 1;
}
for block in slots.drain(..).flatten() {
unsafe { free_block(a, block) };
}
black_box(&ops);
ops
}
unsafe fn mstress_worker<A: GlobalAlloc>(
a: &A,
seed: u64,
rounds: usize,
block_count: usize,
senders: &[Sender<Block>],
rx: &Receiver<Block>,
self_idx: usize,
) -> u64 {
let mut rng = XorShift64::new(seed);
let mut ops: u64 = 0;
let n_threads = senders.len();
for _ in 0..rounds {
unsafe { drain_mailbox(a, rx, &mut ops) };
let mut blocks: Vec<Option<Block>> = Vec::with_capacity(block_count);
for _ in 0..block_count {
blocks.push(Some(unsafe { alloc_block(a, &mut rng) }));
}
let half = block_count / 2;
for _ in 0..half {
let idx = rng.below(block_count);
if let Some(block) = blocks[idx].take() {
if n_threads > 1 && rng.below(8) == 0 {
let mut target = rng.below(n_threads);
if target == self_idx {
target = (target + 1) % n_threads;
}
let _ = senders[target].send(block);
} else {
unsafe { free_block(a, block) };
}
ops += 1;
}
}
for slot in blocks.iter_mut() {
if slot.is_none() {
*slot = Some(unsafe { alloc_block(a, &mut rng) });
}
}
for block in blocks.drain(..).flatten() {
unsafe { free_block(a, block) };
ops += 1;
}
}
black_box(&ops);
ops
}
#[derive(Clone, Copy)]
enum Workload {
Larson,
Mstress,
}
fn run_config<A>(workload: Workload, threads: usize, steps_per_thread: usize, pinned: bool) -> f64
where
A: ZstAlloc + GlobalAlloc + Send + 'static,
{
#[cfg(feature = "pinning")]
let cores = if pinned {
sefer_alloc::PinnedRunner::available_cores()
} else {
None
};
#[cfg(not(feature = "pinning"))]
let _ = pinned; let mut senders: Vec<Sender<Block>> = Vec::with_capacity(threads);
let mut receivers: Vec<Option<Receiver<Block>>> = Vec::with_capacity(threads);
for _ in 0..threads {
let (tx, rx) = channel::<Block>();
senders.push(tx);
receivers.push(Some(rx));
}
let senders = Arc::new(senders);
let barrier = Arc::new(Barrier::new(threads + 1));
let working_set = 768; let mstress_blocks = 512;
let mut handles = Vec::with_capacity(threads);
for (t, rx_slot) in receivers.iter_mut().enumerate() {
let senders = Arc::clone(&senders);
let barrier = Arc::clone(&barrier);
let rx = rx_slot.take().unwrap();
let seed = 0x9E37_79B9_7F4A_7C15u64
.wrapping_mul(t as u64 + 1)
.wrapping_add(0xDEAD_BEEF);
let alloc = A::default_zst();
#[cfg(feature = "pinning")]
let core = cores.as_ref().and_then(|cs| cs.get(t % cs.len()).copied());
let handle = thread::spawn(move || {
#[cfg(feature = "pinning")]
if let Some(core) = core {
let _ = sefer_alloc::PinnedRunner::pin_current_thread_to_core(core);
}
barrier.wait();
let ops = unsafe {
match workload {
Workload::Larson => larson_worker(
&alloc,
seed,
steps_per_thread,
working_set,
&senders,
&rx,
t,
),
Workload::Mstress => mstress_worker(
&alloc,
seed,
steps_per_thread / mstress_blocks.max(1) + 1,
mstress_blocks,
&senders,
&rx,
t,
),
}
};
let mut extra = 0u64;
unsafe { drain_mailbox(&alloc, &rx, &mut extra) };
ops + extra
});
handles.push(handle);
}
barrier.wait();
let start = Instant::now();
let mut total_ops: u64 = 0;
for h in handles {
total_ops += h.join().expect("worker panicked");
}
let elapsed = start.elapsed();
drop(senders);
total_ops as f64 / elapsed.as_secs_f64()
}
trait ZstAlloc {
fn default_zst() -> Self;
}
impl ZstAlloc for SeferMalloc {
fn default_zst() -> Self {
SeferMalloc::new()
}
}
impl ZstAlloc for mimalloc::MiMalloc {
fn default_zst() -> Self {
mimalloc::MiMalloc
}
}
impl ZstAlloc for System {
fn default_zst() -> Self {
System
}
}
fn run_sweep(steps_per_thread: usize, thread_sweep: &[usize], pinned: bool) {
for &workload in &[Workload::Larson, Workload::Mstress] {
let name = match workload {
Workload::Larson => "larson",
Workload::Mstress => "mstress",
};
let mode = if pinned { "pinned" } else { "unpinned" };
println!("--- workload: {name} (mode: {mode}) ---");
println!(
"{:>3} {:>16} {:>16} {:>16}",
"T", "SeferMalloc", "mimalloc", "System"
);
for &t in thread_sweep {
let sefer = run_config::<SeferMalloc>(workload, t, steps_per_thread, pinned);
let mi = run_config::<mimalloc::MiMalloc>(workload, t, steps_per_thread, pinned);
let sys = run_config::<System>(workload, t, steps_per_thread, pinned);
println!(
"{:>3} {:>14.2} M {:>14.2} M {:>14.2} M",
t,
sefer / 1e6,
mi / 1e6,
sys / 1e6
);
}
println!();
}
}
fn main() {
println!("== sefer-alloc MT macro-benchmark ==");
println!("Deterministic xorshift PRNG (fixed seeds); aggregate ops/sec.");
println!("op = one alloc+free pair. Higher is better.\n");
let steps_per_thread = 400_000usize;
let thread_sweep = [1usize, 2, 4];
#[cfg(feature = "pinning")]
{
let cores = sefer_alloc::PinnedRunner::available_cores();
match &cores {
Some(cs) => println!(
"[pinning] host reports {} core id(s); worker i pinned to core i (round-robin).\n",
cs.len()
),
None => println!(
"[pinning] host refused core enumeration; pinned mode falls back to unpinned.\n"
),
}
println!("===== BASELINE (unpinned) =====\n");
run_sweep(steps_per_thread, &thread_sweep, false);
println!("===== PINNED (heap == core) =====\n");
run_sweep(steps_per_thread, &thread_sweep, true);
}
#[cfg(not(feature = "pinning"))]
{
run_sweep(steps_per_thread, &thread_sweep, false);
}
println!("(M = million ops/sec. RSS is not measured here — no portable,");
println!(" dependency-free peak-RSS probe across Win/Linux/macOS; would");
println!(" require platform syscalls. Reported honestly as N/A.)");
}