mod common;
use std::cell::Cell;
use std::time::Duration;
use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion, Throughput};
use lmflow::{Graph, Packet, Timestamp};
const BATCH: u64 = 256;
fn sink_chain_yaml(depth: usize, pool: usize) -> String {
sink_chain_yaml_stats(depth, pool, "basic")
}
fn sink_chain_yaml_stats(depth: usize, pool: usize, stats: &str) -> String {
let mut s = format!("max_queue_size: 1000000\nstats: {stats}\n");
if pool > 0 {
s += &format!(
"executors:\n - {{ name: \"cpu\", type: \"ThreadPoolExecutor\", num_threads: {pool} }}\n"
);
} else {
s += "executors:\n - { name: \"host\", type: \"DelegatingExecutor\" }\n";
}
s += "nodes:\n";
let exec = if pool > 0 {
", executor: \"cpu\""
} else {
", executor: \"host\""
};
for i in 0..depth {
let inp = if i == 0 {
"in".to_string()
} else {
format!("e{}", i - 1)
};
s += &format!(
" - {{ name: \"n{i}\", kernel: \"BenchPassThrough\", input_ports: [\"{inp}\"], output_ports: [\"e{i}\"]{exec} }}\n"
);
}
s += &format!(
" - {{ name: \"sink\", kernel: \"BenchSink\", input_ports: [\"e{}\"], output_ports: []{exec} }}\n",
depth - 1
);
s += "input_ports: [\"in\"]\noutput_ports: []\n";
s
}
fn bench_dispatch(c: &mut Criterion) {
common::register_bench_kernels();
let mut g = c.benchmark_group("dispatch");
g.throughput(Throughput::Elements(BATCH));
for (label, pool) in [("main_thread", 0usize), ("pool1", 1), ("pool4", 4)] {
for depth in [1usize, 16] {
let graph = Graph::from_yaml(&sink_chain_yaml(depth, pool)).unwrap();
graph.start().unwrap();
let input = graph.input("in").unwrap();
let ts = Cell::new(0i64);
g.bench_with_input(
BenchmarkId::new(format!("sink/{label}/depth"), depth),
&depth,
|b, _| {
b.iter(|| {
for _ in 0..BATCH {
let t = ts.get();
ts.set(t + 1);
input.send(Packet::from_i64(0).at(Timestamp(t))).unwrap();
}
graph
.wait_until_idle_timeout(Duration::from_secs(30))
.unwrap();
});
},
);
}
}
g.finish();
}
fn bench_enqueue_only(c: &mut Criterion) {
common::register_bench_kernels();
let mut g = c.benchmark_group("dispatch");
g.throughput(Throughput::Elements(BATCH));
g.bench_function("enqueue_only_paused", |b| {
b.iter_batched(
|| {
let graph = Graph::from_yaml(&sink_chain_yaml(1, 0)).unwrap();
graph.start().unwrap();
graph.pause(); graph
},
|graph| {
let input = graph.input("in").unwrap();
for i in 0..BATCH {
input
.send(Packet::from_i64(0).at(Timestamp(i as i64)))
.unwrap();
}
graph
},
criterion::BatchSize::SmallInput,
);
});
g.finish();
}
fn bench_stats_cost(c: &mut Criterion) {
common::register_bench_kernels();
let mut g = c.benchmark_group("dispatch");
g.throughput(Throughput::Elements(BATCH));
for level in ["full", "basic", "off"] {
let graph = Graph::from_yaml(&sink_chain_yaml_stats(16, 0, level)).unwrap();
graph.start().unwrap();
let input = graph.input("in").unwrap();
let ts = Cell::new(0i64);
g.bench_function(format!("stats/{level}/depth16"), |b| {
b.iter(|| {
for _ in 0..BATCH {
let t = ts.get();
ts.set(t + 1);
input.send(Packet::from_i64(0).at(Timestamp(t))).unwrap();
}
graph
.wait_until_idle_timeout(Duration::from_secs(30))
.unwrap();
});
});
}
g.finish();
}
criterion_group!(
benches,
bench_enqueue_only,
bench_dispatch,
bench_stats_cost
);
criterion_main!(benches);