chore: initial release v0.1.0

Runtime-agnostic oneshot broadcast channel for Rust.
This commit is contained in:
swaits committed 2025-10-13 19:36:37 -06:00
commit e8e55b7676
64 files changed
+23966

No files matched your search

+201
View File
@@ -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);