Skip to content
Merged
Changes from 1 commit
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
50 changes: 45 additions & 5 deletions tests/bench-integration/benches/integration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,20 +10,60 @@
// recursion limit; matches `src/lib.rs` and the e2e crate.
#![recursion_limit = "512"]

use std::alloc::{GlobalAlloc, Layout, System};
use std::sync::OnceLock;
use std::sync::atomic::{AtomicU64, Ordering};

use e2e_tests::{TestClient, text_msg};
use tokio::sync::Mutex;
use whatsapp_rust::Jid;

// Deterministic allocator for CodSpeed's memory instrument: `realloc` always
// allocates a fresh block and copies, never growing in place. Whether the system
// allocator can grow in place depends on the live heap layout, which differs
// run-to-run and would otherwise be charged to the benchmark as memory noise (the
// CodSpeed `reducing-variance` recipe). Bench-only; production keeps the default.
struct DeterministicAlloc;

unsafe impl GlobalAlloc for DeterministicAlloc {
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
unsafe { System.alloc(layout) }
}

unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
unsafe { System.dealloc(ptr, layout) }
}

unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 {
unsafe { System.alloc_zeroed(layout) }
}

unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 {
unsafe {
let new_layout = Layout::from_size_align_unchecked(new_size, layout.align());
let new_ptr = System.alloc(new_layout);
if !new_ptr.is_null() {
std::ptr::copy_nonoverlapping(ptr, new_ptr, layout.size().min(new_size));
System.dealloc(ptr, layout);
}
new_ptr
}
}
}

#[global_allocator]
static GLOBAL: DeterministicAlloc = DeterministicAlloc;

fn main() {
divan::main();
}

// A single multi-thread runtime shared by every bench. Building one per
// iteration would charge thread-pool startup syscalls to the measured region,
// and the real client expects a multi-thread scheduler.
// A single current-thread runtime shared by every bench. CodSpeed runs only the
// Valgrind simulation+memory instruments, which serialize threads onto one core, so a
// multi-thread scheduler buys no measurable signal — only variance, since its worker
// count tracks the runner's CPU count (per-runner thread stacks + scheduling). The
// client has no `block_in_place`, so current_thread drives it fine. Built once so
// runtime startup isn't charged to the measured region.
static RT: OnceLock<tokio::runtime::Runtime> = OnceLock::new();

fn rt() -> &'static tokio::runtime::Runtime {
Expand All @@ -33,10 +73,10 @@ fn rt() -> &'static tokio::runtime::Runtime {
env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("warn"))
.try_init()
.ok();
tokio::runtime::Builder::new_multi_thread()
tokio::runtime::Builder::new_current_thread()
Comment thread
jlucaso1 marked this conversation as resolved.
Outdated
.enable_all()
.build()
.expect("build multi-thread bench runtime")
.expect("build current-thread bench runtime")
})
}

Expand Down
Loading