use std::time::Duration;
use criterion::{BenchmarkId, Criterion, Throughput, black_box, criterion_group, criterion_main};
use sqlitegraph::backend::native::adjacency::are_clusters_contiguous;
const MEASURE: Duration = Duration::from_millis(500);
const WARM_UP: Duration = Duration::from_millis(300);
const CLUSTER_SIZE: u32 = 4096;
fn bench_contiguous_clusters(criterion: &mut Criterion) {
let mut group = criterion.benchmark_group("fragmentation/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;
for _ in 0..count {
offsets.push((current_offset, CLUSTER_SIZE));
current_offset += CLUSTER_SIZE as u64;
}
let total_bytes = current_offset - 1024;
group.throughput(Throughput::Bytes(total_bytes));
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_gapped_clusters_fixed(criterion: &mut Criterion) {
let mut group = criterion.benchmark_group("fragmentation/gapped_fixed");
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 gap_size = 4096u64;
for _ in 0..count {
offsets.push((current_offset, CLUSTER_SIZE));
current_offset += CLUSTER_SIZE as u64 + gap_size;
}
let total_bytes = current_offset - 1024;
group.throughput(Throughput::Bytes(total_bytes));
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_gapped_clusters_variable(criterion: &mut Criterion) {
let mut group = criterion.benchmark_group("fragmentation/gapped_variable");
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 gap_pattern = [2048u64, 4096u64, 8192u64]; let mut gap_index = 0;
for _ in 0..count {
offsets.push((current_offset, CLUSTER_SIZE));
current_offset += CLUSTER_SIZE as u64 + gap_pattern[gap_index];
gap_index = (gap_index + 1) % gap_pattern.len();
}
let total_bytes = current_offset - 1024;
group.throughput(Throughput::Bytes(total_bytes));
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_random_gaps(criterion: &mut Criterion) {
let mut group = criterion.benchmark_group("fragmentation/random");
group.measurement_time(MEASURE);
group.warm_up_time(WARM_UP);
let fragmentation_scores = [0.1, 0.3, 0.5, 0.7];
let cluster_count = 100;
for &frag_score in &fragmentation_scores {
let mut offsets = Vec::with_capacity(cluster_count);
let mut current_offset = 1024u64;
let cluster_bytes: u64 = cluster_count as u64 * CLUSTER_SIZE as u64;
let target_gap_bytes = (frag_score * cluster_bytes as f64 / (1.0 - frag_score)) as u64;
let avg_gap_size = target_gap_bytes / cluster_count as u64;
for i in 0..cluster_count {
offsets.push((current_offset, CLUSTER_SIZE));
let gap_variation = ((i as f64 * 0.5).sin().abs() * avg_gap_size as f64) as u64;
let gap_size = avg_gap_size / 2 + gap_variation;
current_offset += CLUSTER_SIZE as u64 + gap_size;
}
let total_bytes = current_offset - 1024;
let actual_frag =
target_gap_bytes as f64 / (cluster_bytes as f64 + target_gap_bytes as f64);
group.throughput(Throughput::Bytes(total_bytes));
group.bench_with_input(
BenchmarkId::new(format!("frag_{:.1}", actual_frag), frag_score),
&cluster_count,
|b, &_count| {
b.iter(|| {
black_box(are_clusters_contiguous(black_box(&offsets)));
});
},
);
}
group.finish();
}
fn bench_worst_case_fragmentation(criterion: &mut Criterion) {
let mut group = criterion.benchmark_group("fragmentation/worst_case");
group.measurement_time(MEASURE);
group.warm_up_time(WARM_UP);
let cluster_counts = [10, 50, 100];
for &count in &cluster_counts {
let mut offsets = Vec::with_capacity(count);
let mut current_offset = 1024u64;
let gap_size = 65536u64;
for _ in 0..count {
offsets.push((current_offset, CLUSTER_SIZE));
current_offset += CLUSTER_SIZE as u64 + gap_size;
}
let total_bytes = current_offset - 1024;
group.throughput(Throughput::Bytes(total_bytes));
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_mixed_regions(criterion: &mut Criterion) {
let mut group = criterion.benchmark_group("fragmentation/mixed");
group.measurement_time(MEASURE);
group.warm_up_time(WARM_UP);
let total_clusters = 100;
let mut offsets = Vec::with_capacity(total_clusters);
let mut current_offset = 1024u64;
let mut i = 0;
while i < total_clusters {
let contiguous_len = 20.min(total_clusters - i);
for _ in 0..contiguous_len {
offsets.push((current_offset, CLUSTER_SIZE));
current_offset += CLUSTER_SIZE as u64;
}
i += contiguous_len;
let gapped_len = 10.min(total_clusters - i);
for _ in 0..gapped_len {
offsets.push((current_offset, CLUSTER_SIZE));
current_offset += CLUSTER_SIZE as u64 + 4096;
}
i += gapped_len;
}
let total_bytes = current_offset - 1024;
group.throughput(Throughput::Bytes(total_bytes));
group.bench_function("mixed_pattern", |b| {
b.iter(|| {
black_box(are_clusters_contiguous(black_box(&offsets)));
});
});
group.finish();
}
criterion_group!(
fragmentation_benches,
bench_contiguous_clusters,
bench_gapped_clusters_fixed,
bench_gapped_clusters_variable,
bench_random_gaps,
bench_worst_case_fragmentation,
bench_mixed_regions
);
criterion_main!(fragmentation_benches);