use std::sync::Arc;
use std::thread;
use std::time::{Duration, Instant};
use zeropool::BufferPool;
#[allow(missing_docs)]
const NUM_STRESS_THREADS: usize = 12;
const STRESS_DURATION_SECS: u64 = 30;
const BURST_SIZE: usize = 200;
const THRASH_ITERATIONS: usize = 50000;
const SMALL_SIZES: &[usize] = &[64, 128, 256, 512];
const MEDIUM_SIZES: &[usize] = &[4096, 8192, 16384, 32768];
const LARGE_SIZES: &[usize] = &[65_536, 131_072, 262_144, 524_288];
const HUGE_SIZES: &[usize] = &[1_048_576, 2_097_152, 4_194_304];
#[derive(Clone, Copy)]
enum StressPhase {
BurstAllocations,
CacheThrashing,
MixedSizes,
EvictionPressure,
RandomChaos,
}
impl StressPhase {
fn name(self) -> &'static str {
match self {
StressPhase::BurstAllocations => "Burst Allocations",
StressPhase::CacheThrashing => "Cache Thrashing",
StressPhase::MixedSizes => "Mixed Sizes",
StressPhase::EvictionPressure => "Eviction Pressure",
StressPhase::RandomChaos => "Random Chaos",
}
}
fn all() -> &'static [StressPhase] {
&[
StressPhase::BurstAllocations,
StressPhase::CacheThrashing,
StressPhase::MixedSizes,
StressPhase::EvictionPressure,
StressPhase::RandomChaos,
]
}
}
fn main() {
eprintln!("=== Stress Test Profiling ===");
eprintln!("Stress threads: {NUM_STRESS_THREADS}");
eprintln!("Duration: {STRESS_DURATION_SECS}s");
eprintln!("Phases: {}", StressPhase::all().len());
eprintln!();
let pool = Arc::new(
BufferPool::builder()
.num_shards(16)
.tls_cache_size(8)
.max_buffers_per_shard(64)
.min_buffer_size(64)
.build(),
);
let total_start = Instant::now();
for phase in StressPhase::all() {
eprintln!("\n=== Phase: {} ===", phase.name());
let phase_start = Instant::now();
run_stress_phase(&pool, *phase);
let phase_duration = phase_start.elapsed();
eprintln!("[{}] Completed in {:.2?}", phase.name(), phase_duration);
eprintln!("Pool stats: {} buffers", pool.len());
}
let total_duration = total_start.elapsed();
eprintln!("\n=== Stress Test Complete ===");
eprintln!("Total time: {total_duration:.2?}");
eprintln!("Final pool size: {} buffers", pool.len());
}
fn run_stress_phase(pool: &Arc<BufferPool>, phase: StressPhase) {
let mut handles = Vec::new();
for thread_id in 0..NUM_STRESS_THREADS {
let pool = Arc::clone(pool);
let handle = thread::spawn(move || stress_worker(thread_id, &pool, phase));
handles.push(handle);
}
for handle in handles {
handle.join().unwrap();
}
}
fn stress_worker(thread_id: usize, pool: &Arc<BufferPool>, phase: StressPhase) {
match phase {
StressPhase::BurstAllocations => burst_allocations(pool, thread_id),
StressPhase::CacheThrashing => cache_thrashing(pool, thread_id),
StressPhase::MixedSizes => mixed_sizes(pool, thread_id),
StressPhase::EvictionPressure => eviction_pressure(pool, thread_id),
StressPhase::RandomChaos => random_chaos(pool, thread_id),
}
}
fn burst_allocations(pool: &BufferPool, _thread_id: usize) {
for burst_round in 0..10 {
let mut buffers = Vec::new();
for _ in 0..BURST_SIZE {
let size = MEDIUM_SIZES[burst_round % MEDIUM_SIZES.len()];
buffers.push(pool.get(size));
}
for buffer in &mut buffers {
process_buffer(buffer);
}
drop(buffers);
thread::sleep(Duration::from_millis(10));
}
}
fn cache_thrashing(pool: &BufferPool, thread_id: usize) {
let pattern = if thread_id.is_multiple_of(2) {
&[4096, 65536, 4096, 65536] } else {
&[16_384, 262_144, 16_384, 262_144] };
for _ in 0..THRASH_ITERATIONS {
for &size in pattern {
let mut buffer = pool.get(size);
process_buffer(&mut buffer);
}
}
}
fn mixed_sizes(pool: &BufferPool, thread_id: usize) {
let seed = thread_id;
for i in 0..THRASH_ITERATIONS {
let category = (seed + i) % 4;
let size = match category {
0 => SMALL_SIZES[(seed * 7 + i) % SMALL_SIZES.len()],
1 => MEDIUM_SIZES[(seed * 11 + i) % MEDIUM_SIZES.len()],
2 => LARGE_SIZES[(seed * 13 + i) % LARGE_SIZES.len()],
_ => HUGE_SIZES[(seed * 17 + i) % HUGE_SIZES.len()],
};
let mut buffer = pool.get(size);
process_buffer(&mut buffer);
}
}
fn eviction_pressure(pool: &BufferPool, _thread_id: usize) {
for round in 0..20 {
let mut buffers = Vec::new();
for i in 0..100 {
let size = LARGE_SIZES[i % LARGE_SIZES.len()];
buffers.push(pool.get(size));
}
for buffer in &mut buffers {
process_buffer(buffer);
}
buffers.truncate(50);
if round % 5 == 0 {
for _ in 0..10 {
let mut huge = pool.get(HUGE_SIZES[0]);
process_buffer(&mut huge);
}
}
drop(buffers);
}
}
fn random_chaos(pool: &BufferPool, thread_id: usize) {
let mut pseudo_random = thread_id.wrapping_mul(1_103_515_245).wrapping_add(12345);
for _ in 0..THRASH_ITERATIONS {
pseudo_random = pseudo_random.wrapping_mul(1_103_515_245).wrapping_add(12345);
let size_category = (pseudo_random >> 16) % 20;
let size = match size_category {
0..=7 => SMALL_SIZES[(pseudo_random >> 8) % SMALL_SIZES.len()],
8..=14 => MEDIUM_SIZES[(pseudo_random >> 8) % MEDIUM_SIZES.len()],
15..=18 => LARGE_SIZES[(pseudo_random >> 8) % LARGE_SIZES.len()],
_ => HUGE_SIZES[(pseudo_random >> 8) % HUGE_SIZES.len()],
};
let mut buffer = pool.get(size);
process_buffer(&mut buffer);
if pseudo_random.is_multiple_of(10) {
let mut held_buffers = Vec::new();
for _ in 0..5 {
held_buffers.push(pool.get(size / 2));
}
for buf in &mut held_buffers {
process_buffer(buf);
}
}
}
}
fn process_buffer(buffer: &mut [u8]) {
std::hint::black_box(buffer.len());
if !buffer.is_empty() {
buffer[0] = 1;
if buffer.len() > 1 {
buffer[buffer.len() - 1] = 1;
}
}
}