202 lines
6.7 KiB
Rust
202 lines
6.7 KiB
Rust
//! 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::<Arc<i32>>::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::<Arc<i32>>::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);
|