//! Linearity and scaling benchmarks //! //! Proves O(N) linear scaling behavior for announcement operations: //! - Announce operation: O(1) operation + O(N) listener wakeup //! - Retrieval operation: O(1) per listener = O(N) total for N listeners //! - Listener creation: O(1) per listener = O(N) total for N listeners //! //! Tests scales from 0 to 10 million listeners to verify linearity holds. use announcement::Announcement; use criterion::{ criterion_group, criterion_main, BenchmarkId, Criterion, PlotConfiguration, Throughput, }; use std::hint::black_box; use std::sync::Arc; /// Benchmark: Linearity of announce operation with 0 to 10M listeners /// /// This verifies that announcing scales linearly with listener count. /// Expected: O(1) announce + O(N) wakeup = O(N) total fn bench_linearity_announce(c: &mut Criterion) { let mut group = c.benchmark_group("scaling/announce_linearity"); group.plot_config(PlotConfiguration::default().summary_scale(criterion::AxisScale::Linear)); // Test 3 logarithmically-spaced scales to prove O(N) linearity // Mathematical proof: 3 points on log scale demonstrate linear behavior let scales = [0, 1, 1_000, 100_000]; for n in scales.iter() { group.throughput(Throughput::Elements(*n as u64)); group.bench_with_input(BenchmarkId::from_parameter(n), n, |b, &n| { b.iter(|| { let (tx, announcement) = Announcement::new(); // Create N listeners let mut listeners = Vec::with_capacity(n); for _ in 0..n { listeners.push(announcement.listener()); } // Measure announce time (should be O(1) + O(N) wakeup) tx.announce(Arc::new(42)).unwrap(); // Keep listeners alive to measure full wakeup cost black_box(listeners); }); }); } group.finish(); } /// Benchmark: Linearity of retrieval operation with 1 to 10M listeners /// /// This verifies that retrieving from N listeners scales linearly. /// Expected: O(1) per listener × N listeners = O(N) total fn bench_linearity_retrieval(c: &mut Criterion) { let mut group = c.benchmark_group("scaling/retrieval_linearity"); group.plot_config(PlotConfiguration::default().summary_scale(criterion::AxisScale::Linear)); let scales = [1, 1_000, 100_000]; for n in scales.iter() { group.throughput(Throughput::Elements(*n as u64)); group.bench_with_input(BenchmarkId::from_parameter(n), n, |b, &n| { b.iter(|| { let (tx, announcement) = Announcement::new(); // Create N listeners let mut listeners = Vec::with_capacity(n); for _ in 0..n { listeners.push(announcement.listener()); } // Announce first tx.announce(Arc::new(42)).unwrap(); // Measure retrieval time (should be O(1) per listener = O(N) total) for listener in &listeners { black_box(listener.try_listen()); } }); }); } group.finish(); } /// Benchmark: Linearity of listener creation with 1 to 10M listeners /// /// This verifies that creating N listeners scales linearly. /// Expected: O(1) per listener × N listeners = O(N) total fn bench_linearity_listener_creation(c: &mut Criterion) { let mut group = c.benchmark_group("scaling/listener_creation_linearity"); group.plot_config(PlotConfiguration::default().summary_scale(criterion::AxisScale::Linear)); let scales = [1, 1_000, 100_000]; for n in scales.iter() { group.throughput(Throughput::Elements(*n as u64)); group.bench_with_input(BenchmarkId::from_parameter(n), n, |b, &n| { b.iter(|| { let (_tx, announcement) = Announcement::>::new(); // Create N listeners (should be O(N) with O(1) per listener) let mut listeners = Vec::with_capacity(n); for _ in 0..n { listeners.push(announcement.listener()); } black_box(listeners); }); }); } group.finish(); } /// Benchmark: Zero listener overhead /// /// Critical test: Announcing with 0 listeners MUST be nearly free (<5ns). /// This is a key selling point of the design. fn bench_zero_listener_overhead(c: &mut Criterion) { let mut group = c.benchmark_group("scaling/zero_listener_overhead"); group.plot_config(PlotConfiguration::default().summary_scale(criterion::AxisScale::Linear)); // Compare 0 vs 1 vs 1000 listeners // Zero listeners MUST be nearly free (<5ns) let listener_counts = [0, 1, 1_000]; for n in listener_counts.iter() { group.bench_with_input(BenchmarkId::from_parameter(n), n, |b, &n| { b.iter(|| { let (tx, announcement) = Announcement::new(); // Create N listeners let mut listeners = Vec::with_capacity(n); for _ in 0..n { listeners.push(announcement.listener()); } // Announce with N listeners tx.announce(Arc::new(42)).unwrap(); black_box(listeners); }); }); } group.finish(); } /// Benchmark: Overhead breakdown /// /// Measures individual components to understand where time is spent. fn bench_overhead_breakdown(c: &mut Criterion) { let mut group = c.benchmark_group("scaling/overhead_breakdown"); // Measure individual components group.bench_function("just_creation", |b| { b.iter(|| { let (_tx, _announcement) = Announcement::>::new(); }); }); group.bench_function("creation_plus_announce", |b| { b.iter(|| { let (tx, _announcement) = Announcement::new(); tx.announce(Arc::new(42)).unwrap(); }); }); group.bench_function("with_1_listener", |b| { b.iter(|| { let (tx, announcement) = Announcement::new(); let _listener = announcement.listener(); tx.announce(Arc::new(42)).unwrap(); }); }); group.bench_function("with_1_listener_retrieve", |b| { b.iter(|| { let (tx, announcement) = Announcement::new(); let listener = announcement.listener(); tx.announce(Arc::new(42)).unwrap(); black_box(listener.try_listen()); }); }); group.finish(); } criterion_group!( benches, bench_linearity_announce, bench_linearity_retrieval, bench_linearity_listener_creation, bench_zero_listener_overhead, bench_overhead_breakdown, ); criterion_main!(benches);