use criterion::{BenchmarkId, Criterion, Throughput, criterion_group, criterion_main};
use std::hint::black_box;
use std::sync::Arc;
use yo_index::RawMap;
use yo_reactor::{BATCH_MAX, Engine, Flow, Reactor};
use yo_shard::Epochs;
use yo_shard::spsc::{Sender, lane};
const SIZES: [usize; 2] = [100_000, 10_000_000];
fn sizes() -> &'static [usize] {
if std::env::var_os("YO_BENCH_SMOKE").is_some() {
&[1_000]
} else {
&SIZES
}
}
const BATCH: usize = BATCH_MAX;
fn key(i: usize) -> Vec<u8> {
format!("key:{i:012}").into_bytes()
}
struct Lookup {
map: RawMap,
keys: Vec<Vec<u8>>,
hashes: Vec<u64>,
warm: bool,
found: u64,
flushes: u64,
}
impl Lookup {
fn new(n: usize, warm: bool) -> Lookup {
let mut map = RawMap::new();
for i in 0..n {
map.set(&key(i), b"0123456789abcdef0123456789abcdef");
}
let keys: Vec<Vec<u8>> = (0..1024).map(|i| key(i * 7919 % n)).collect();
let hashes = keys.iter().map(|k| RawMap::hash_of(k)).collect();
Lookup {
map,
keys,
hashes,
warm,
found: 0,
flushes: 0,
}
}
}
impl Engine for Lookup {
type Work = u32;
fn key_hash(&self, work: &u32) -> Option<u64> {
Some(self.hashes[*work as usize])
}
fn prefetch(&self, _work: &Self::Work, hash: u64) {
if self.warm {
self.map.prefetch(hash);
}
}
fn run(&mut self, work: u32, hash: Option<u64>) -> Flow {
let k = &self.keys[work as usize];
if self.map.get_hashed(hash.unwrap(), k).is_some() {
self.found += 1;
}
Flow::Next
}
fn flush(&mut self) {
self.flushes += 1;
}
}
fn batch(round: usize) -> Vec<u32> {
(0..BATCH)
.map(|i| ((round * BATCH + i * 13) % 1024) as u32)
.collect()
}
fn bench_inline(c: &mut Criterion) {
let mut g = c.benchmark_group("inline");
g.throughput(Throughput::Elements(1));
for &n in sizes() {
let mut r = Reactor::inline(Lookup::new(n, true));
g.bench_with_input(BenchmarkId::new("execute", n), &n, |bench, _| {
let mut i = 0u32;
bench.iter(|| {
i = (i + 1) & 1023;
black_box(r.execute(black_box(i)))
});
});
}
g.finish();
}
fn bench_batch(c: &mut Criterion) {
let mut g = c.benchmark_group("batch");
g.throughput(Throughput::Elements(BATCH as u64));
for &n in sizes() {
let mut r = Reactor::inline(Lookup::new(n, true));
let rounds: Vec<Vec<u32>> = (0..16).map(batch).collect();
for (name, warm) in [("warm", true), ("cold", false)] {
r.engine_mut().warm = warm;
g.bench_with_input(BenchmarkId::new(name, n), &n, |bench, _| {
let mut round = 0usize;
bench.iter(|| {
round = (round + 1) & 15;
black_box(r.execute_all(rounds[round].iter().copied()))
});
});
}
}
g.finish();
}
fn bench_loop(c: &mut Criterion) {
let mut g = c.benchmark_group("loop");
g.throughput(Throughput::Elements(BATCH as u64));
for &n in sizes() {
let (tx, rx): (Sender<u32>, _) = lane(4096);
let epochs = Epochs::new(1);
let mut r = Reactor::new(Lookup::new(n, true), 0, Arc::clone(&epochs), vec![rx]);
let rounds: Vec<Vec<u32>> = (0..16).map(batch).collect();
g.bench_with_input(BenchmarkId::new("tick", n), &n, |bench, _| {
let mut round = 0usize;
bench.iter(|| {
round = (round + 1) & 15;
for w in &rounds[round] {
tx.push(*w).unwrap();
}
black_box(r.tick().unwrap())
});
});
}
g.finish();
}
criterion_group!(benches, bench_inline, bench_batch, bench_loop);
criterion_main!(benches);