chore: initial release v0.1.0
Runtime-agnostic oneshot broadcast channel for Rust.
This commit is contained in:
64 files changed
+23966
No files matched your search
@@ -0,0 +1,201 @@
|
||||
//! 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);
|
||||
Reference in new issue
Block a user