Files
swaits e8e55b7676 chore: initial release v0.1.0
Runtime-agnostic oneshot broadcast channel for Rust.
2025-10-13 19:36:37 -06:00

285 lines
8.2 KiB
Rust

use announcement::Announcement;
use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion, Throughput};
use std::collections::HashMap;
use std::hint::black_box;
use std::sync::Arc;
// Small type: 4 bytes
fn bench_small_type(c: &mut Criterion) {
let mut group = c.benchmark_group("small_type_i32");
for n in [1, 10, 100, 500].iter() {
group.throughput(Throughput::Elements(*n as u64));
// Direct Clone
group.bench_with_input(BenchmarkId::new("direct_clone", n), n, |b, &n| {
b.iter(|| {
let (tx, announcement) = Announcement::new();
let mut listeners = Vec::new();
for _ in 0..n {
listeners.push(announcement.listener());
}
tx.announce(42i32).unwrap();
for listener in listeners {
black_box(listener.try_listen());
}
});
});
// Arc<i32> Clone
group.bench_with_input(BenchmarkId::new("arc_clone", n), n, |b, &n| {
b.iter(|| {
let (tx, announcement) = Announcement::new();
let mut listeners = Vec::new();
for _ in 0..n {
listeners.push(announcement.listener());
}
tx.announce(Arc::new(42i32)).unwrap();
for listener in listeners {
black_box(listener.try_listen());
}
});
});
}
group.finish();
}
// Medium type: 1KB
fn bench_medium_type(c: &mut Criterion) {
let mut group = c.benchmark_group("medium_type_1kb");
for n in [1, 10, 100, 500].iter() {
group.throughput(Throughput::Elements(*n as u64));
// Direct Clone
group.bench_with_input(BenchmarkId::new("direct_clone", n), n, |b, &n| {
b.iter(|| {
let (tx, announcement) = Announcement::new();
let mut listeners = Vec::new();
for _ in 0..n {
listeners.push(announcement.listener());
}
tx.announce([0u8; 1024]).unwrap();
for listener in listeners {
black_box(listener.try_listen());
}
});
});
// Arc Clone
group.bench_with_input(BenchmarkId::new("arc_clone", n), n, |b, &n| {
b.iter(|| {
let (tx, announcement) = Announcement::new();
let mut listeners = Vec::new();
for _ in 0..n {
listeners.push(announcement.listener());
}
tx.announce(Arc::new([0u8; 1024])).unwrap();
for listener in listeners {
black_box(listener.try_listen());
}
});
});
}
group.finish();
}
// Large type: 1MB
fn bench_large_type(c: &mut Criterion) {
let mut group = c.benchmark_group("large_type_1mb");
group.sample_size(10); // Reduce sample size for large data
for n in [1, 10, 100, 500].iter() {
group.throughput(Throughput::Elements(*n as u64));
// Direct Clone
group.bench_with_input(BenchmarkId::new("direct_clone", n), n, |b, &n| {
b.iter(|| {
let (tx, announcement) = Announcement::new();
let mut listeners = Vec::new();
for _ in 0..n {
listeners.push(announcement.listener());
}
tx.announce(vec![0u8; 1024 * 1024]).unwrap();
for listener in listeners {
black_box(listener.try_listen());
}
});
});
// Arc Clone
group.bench_with_input(BenchmarkId::new("arc_clone", n), n, |b, &n| {
b.iter(|| {
let (tx, announcement) = Announcement::new();
let mut listeners = Vec::new();
for _ in 0..n {
listeners.push(announcement.listener());
}
tx.announce(Arc::new(vec![0u8; 1024 * 1024])).unwrap();
for listener in listeners {
black_box(listener.try_listen());
}
});
});
}
group.finish();
}
// Complex type: Config with String, Vec, HashMap
#[derive(Clone, Debug)]
#[allow(dead_code)]
struct Config {
name: String,
tags: Vec<String>,
settings: HashMap<String, String>,
data: Vec<u8>,
}
impl Config {
fn new() -> Self {
let mut settings = HashMap::new();
settings.insert("key1".to_string(), "value1".to_string());
settings.insert("key2".to_string(), "value2".to_string());
settings.insert("key3".to_string(), "value3".to_string());
Config {
name: "MyConfig".to_string(),
tags: vec!["tag1".to_string(), "tag2".to_string(), "tag3".to_string()],
settings,
data: vec![0u8; 1024],
}
}
}
fn bench_complex_type(c: &mut Criterion) {
let mut group = c.benchmark_group("complex_type_config");
for n in [1, 10, 100, 500].iter() {
group.throughput(Throughput::Elements(*n as u64));
// Direct Clone
group.bench_with_input(BenchmarkId::new("direct_clone", n), n, |b, &n| {
b.iter(|| {
let (tx, announcement) = Announcement::new();
let mut listeners = Vec::new();
for _ in 0..n {
listeners.push(announcement.listener());
}
tx.announce(Config::new()).unwrap();
for listener in listeners {
black_box(listener.try_listen());
}
});
});
// Arc Clone
group.bench_with_input(BenchmarkId::new("arc_clone", n), n, |b, &n| {
b.iter(|| {
let (tx, announcement) = Announcement::new();
let mut listeners = Vec::new();
for _ in 0..n {
listeners.push(announcement.listener());
}
tx.announce(Arc::new(Config::new())).unwrap();
for listener in listeners {
black_box(listener.try_listen());
}
});
});
}
group.finish();
}
// Comparison benchmark showing the crossover point
fn bench_crossover_analysis(c: &mut Criterion) {
let mut group = c.benchmark_group("crossover_analysis");
// Test various type sizes to find where Arc becomes beneficial
let sizes = [4, 8, 16, 32, 64, 128, 256, 512];
for size in sizes.iter() {
group.throughput(Throughput::Bytes(*size as u64));
// Direct clone with 10 listeners
group.bench_with_input(
BenchmarkId::new("direct_10_listeners", size),
size,
|b, &size| {
b.iter(|| {
let (tx, announcement) = Announcement::new();
let mut listeners = Vec::new();
for _ in 0..10 {
listeners.push(announcement.listener());
}
let data = vec![0u8; size];
tx.announce(data).unwrap();
for listener in listeners {
black_box(listener.try_listen());
}
});
},
);
// Arc clone with 10 listeners
group.bench_with_input(
BenchmarkId::new("arc_10_listeners", size),
size,
|b, &size| {
b.iter(|| {
let (tx, announcement) = Announcement::new();
let mut listeners = Vec::new();
for _ in 0..10 {
listeners.push(announcement.listener());
}
let data = Arc::new(vec![0u8; size]);
tx.announce(data).unwrap();
for listener in listeners {
black_box(listener.try_listen());
}
});
},
);
}
group.finish();
}
criterion_group!(
benches,
bench_small_type,
bench_medium_type,
bench_large_type,
bench_complex_type,
bench_crossover_analysis,
);
criterion_main!(benches);