use std::time::Duration;
use criterion::{BenchmarkId, Criterion, Throughput, black_box, criterion_group, criterion_main};
use sqlitegraph::backend::native::{
NativeNodeId,
adjacency::{LinearDetector, are_clusters_contiguous},
};
const MEASURE: Duration = Duration::from_millis(500);
const WARM_UP: Duration = Duration::from_millis(300);
fn bench_observe_overhead(criterion: &mut Criterion) {
let mut group = criterion.benchmark_group("linear_detector/observe");
group.measurement_time(MEASURE);
group.warm_up_time(WARM_UP);
let node_counts = [100, 500, 1000, 5000];
for &count in &node_counts {
group.throughput(Throughput::Elements(count as u64));
group.bench_with_input(BenchmarkId::from_parameter(count), &count, |b, &count| {
b.iter(|| {
let mut detector = LinearDetector::new();
for i in 0..count {
let node_id = i as NativeNodeId;
let degree = 1u32; black_box(detector.observe(black_box(node_id), black_box(degree)));
}
black_box(detector.is_linear_confirmed());
});
});
}
group.finish();
}
fn bench_observe_with_cluster_overhead(criterion: &mut Criterion) {
let mut group = criterion.benchmark_group("linear_detector/observe_with_cluster");
group.measurement_time(MEASURE);
group.warm_up_time(WARM_UP);
let node_counts = [100, 500, 1000, 5000];
for &count in &node_counts {
group.throughput(Throughput::Elements(count as u64));
group.bench_with_input(BenchmarkId::from_parameter(count), &count, |b, &count| {
b.iter(|| {
let mut detector = LinearDetector::new();
let mut current_offset = 1024u64;
let cluster_size = 4096u32;
for i in 0..count {
let node_id = i as NativeNodeId;
let degree = 1u32;
black_box(detector.observe_with_cluster(
black_box(node_id),
black_box(degree),
black_box(current_offset),
black_box(cluster_size),
));
current_offset += cluster_size as u64;
}
black_box(detector.cluster_offsets().len());
black_box(detector.is_linear_confirmed());
});
});
}
group.finish();
}
fn bench_contiguity_validation(criterion: &mut Criterion) {
let mut group = criterion.benchmark_group("linear_detector/contiguity");
group.measurement_time(MEASURE);
group.warm_up_time(WARM_UP);
let cluster_counts = [10, 50, 100, 500];
for &count in &cluster_counts {
let mut offsets = Vec::with_capacity(count);
let mut current_offset = 1024u64;
let cluster_size = 4096u32;
for _ in 0..count {
offsets.push((current_offset, cluster_size));
current_offset += cluster_size as u64;
}
group.bench_with_input(BenchmarkId::from_parameter(count), &count, |b, &_count| {
b.iter(|| {
black_box(are_clusters_contiguous(black_box(&offsets)));
});
});
}
group.finish();
}
fn bench_validate_contiguity_method(criterion: &mut Criterion) {
let mut group = criterion.benchmark_group("linear_detector/validate_method");
group.measurement_time(MEASURE);
group.warm_up_time(WARM_UP);
let cluster_counts = [10, 50, 100, 500];
for &count in &cluster_counts {
let mut offsets = Vec::with_capacity(count);
let mut current_offset = 1024u64;
let cluster_size = 4096u32;
for _ in 0..count {
offsets.push((current_offset, cluster_size));
current_offset += cluster_size as u64;
}
group.bench_with_input(BenchmarkId::from_parameter(count), &count, |b, &_count| {
b.iter(|| {
let mut detector = LinearDetector::new();
for (i, &(offset, size)) in offsets.iter().enumerate() {
let node_id = i as NativeNodeId;
detector.observe_with_cluster(node_id, 1, offset, size);
}
black_box(detector.validate_contiguity());
});
});
}
group.finish();
}
fn bench_non_contiguous_validation(criterion: &mut Criterion) {
let mut group = criterion.benchmark_group("linear_detector/non_contiguous");
group.measurement_time(MEASURE);
group.warm_up_time(WARM_UP);
let cluster_counts = [10, 50, 100, 500];
for &count in &cluster_counts {
let mut offsets = Vec::with_capacity(count);
let mut current_offset = 1024u64;
let cluster_size = 4096u32;
let gap_size = 4096u64;
for i in 0..count {
offsets.push((current_offset, cluster_size));
if i % 2 == 0 {
current_offset += cluster_size as u64 + gap_size;
} else {
current_offset += cluster_size as u64;
}
}
group.bench_with_input(BenchmarkId::from_parameter(count), &count, |b, &_count| {
b.iter(|| {
black_box(are_clusters_contiguous(black_box(&offsets)));
});
});
}
group.finish();
}
fn bench_branching_detection(criterion: &mut Criterion) {
let mut group = criterion.benchmark_group("linear_detector/branching");
group.measurement_time(MEASURE);
group.warm_up_time(WARM_UP);
let branching_positions = [0, 1, 2, 10, 50];
for &pos in &branching_positions {
group.bench_with_input(
BenchmarkId::new(format!("branch_at_{}", pos), pos),
&pos,
|b, &branch_pos| {
b.iter(|| {
let mut detector = LinearDetector::new();
for i in 0..branch_pos {
black_box(detector.observe(i as NativeNodeId, 1));
}
black_box(detector.observe(branch_pos as NativeNodeId, 2));
for i in (branch_pos + 1)..(branch_pos + 10) {
black_box(detector.observe(i as NativeNodeId, 1));
}
black_box(detector.is_linear_confirmed());
black_box(detector.current_pattern());
});
},
);
}
group.finish();
}
criterion_group!(
linear_detector_benches,
bench_observe_overhead,
bench_observe_with_cluster_overhead,
bench_contiguity_validation,
bench_validate_contiguity_method,
bench_non_contiguous_validation,
bench_branching_detection
);
criterion_main!(linear_detector_benches);