use batpak::prelude::*;
use batpak::store::{IndexTopology, Store, StoreConfig};
use batpak_bench_support::{apply_profile, BenchProfile};
use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion, Throughput};
use std::hint::black_box;
use tempfile::TempDir;
const KINDS: [EventKind; 8] = [
EventKind::custom(0x1, 1),
EventKind::custom(0x1, 2),
EventKind::custom(0x1, 3),
EventKind::custom(0x1, 4),
EventKind::custom(0x2, 1),
EventKind::custom(0x2, 2),
EventKind::custom(0x2, 3),
EventKind::custom(0x2, 4),
];
const EVENTS_PER_KIND: u32 = 1_000;
const QUERY_KIND: EventKind = KINDS[0];
const QUERY_CATEGORY: u8 = 0x1;
fn build_sorted_store(topology: IndexTopology) -> (Store, TempDir) {
let dir = TempDir::new().expect("temp dir");
let store = Store::open(
StoreConfig::new(dir.path())
.with_index_topology(topology)
.with_sync_every_n_events(100_000),
)
.expect("open");
for (i, &kind) in KINDS.iter().enumerate() {
let coord = Coordinate::new(format!("bench:entity:{i}"), "bench:scope").expect("coord");
for seq in 0..EVENTS_PER_KIND {
let _ = store
.append(&coord, kind, &serde_json::json!({"seq": seq}))
.expect("append");
}
}
(store, dir)
}
fn build_interleaved_store(topology: IndexTopology) -> (Store, TempDir) {
let dir = TempDir::new().expect("temp dir");
let store = Store::open(
StoreConfig::new(dir.path())
.with_index_topology(topology)
.with_sync_every_n_events(100_000),
)
.expect("open");
let coords: Vec<Coordinate> = KINDS
.iter()
.enumerate()
.map(|(i, _)| Coordinate::new(format!("bench:entity:{i}"), "bench:scope").expect("coord"))
.collect();
for seq in 0..EVENTS_PER_KIND {
for (i, &kind) in KINDS.iter().enumerate() {
let _ = store
.append(&coords[i], kind, &serde_json::json!({"seq": seq}))
.expect("append");
}
}
(store, dir)
}
fn bench_by_kind(c: &mut Criterion) {
let cases = [
("soa", IndexTopology::scan()),
("aosoa64", IndexTopology::tiled()),
];
let mut group = c.benchmark_group("by_kind/sorted");
apply_profile(&mut group, BenchProfile::Heavy);
group.throughput(Throughput::Elements(EVENTS_PER_KIND as u64));
for (name, topology) in &cases {
let (store, _dir) = build_sorted_store(topology.clone());
group.bench_function(BenchmarkId::new(*name, EVENTS_PER_KIND), |b| {
b.iter(|| black_box(store.by_fact(QUERY_KIND)));
});
store.close().expect("close");
}
group.finish();
let mut group = c.benchmark_group("by_kind/interleaved");
apply_profile(&mut group, BenchProfile::Heavy);
group.throughput(Throughput::Elements(EVENTS_PER_KIND as u64));
for (name, topology) in &cases {
let (store, _dir) = build_interleaved_store(topology.clone());
group.bench_function(BenchmarkId::new(*name, EVENTS_PER_KIND), |b| {
b.iter(|| black_box(store.by_fact(QUERY_KIND)));
});
store.close().expect("close");
}
group.finish();
}
fn bench_by_category(c: &mut Criterion) {
let cases = [
("soa", IndexTopology::scan()),
("aosoa64", IndexTopology::tiled()),
];
let mut group = c.benchmark_group("by_category/sorted");
apply_profile(&mut group, BenchProfile::Heavy);
group.throughput(Throughput::Elements(4 * EVENTS_PER_KIND as u64));
for (name, topology) in &cases {
let (store, _dir) = build_sorted_store(topology.clone());
group.bench_function(BenchmarkId::new(*name, EVENTS_PER_KIND), |b| {
b.iter(|| {
black_box(store.query(&Region::all().with_fact_category(
EventCategory::new(QUERY_CATEGORY).expect("valid category"),
)))
});
});
store.close().expect("close");
}
group.finish();
let mut group = c.benchmark_group("by_category/interleaved");
apply_profile(&mut group, BenchProfile::Heavy);
group.throughput(Throughput::Elements(4 * EVENTS_PER_KIND as u64));
for (name, topology) in &cases {
let (store, _dir) = build_interleaved_store(topology.clone());
group.bench_function(BenchmarkId::new(*name, EVENTS_PER_KIND), |b| {
b.iter(|| {
black_box(store.query(&Region::all().with_fact_category(
EventCategory::new(QUERY_CATEGORY).expect("valid category"),
)))
});
});
store.close().expect("close");
}
group.finish();
}
criterion_group!(benches, bench_by_kind, bench_by_category);
criterion_main!(benches);