use std::time::Duration;
use criterion::{BenchmarkId, Criterion, Throughput, black_box, criterion_group, criterion_main};
use sqlitegraph::backend::native::{
EdgeFlags, EdgeRecord,
adjacency::SequentialClusterReader,
types::{NativeBackendError, NativeResult},
v2::edge_cluster::{cluster::EdgeCluster, cluster_trace::Direction},
v2::string_table::StringTable,
};
const MEASURE: Duration = Duration::from_millis(500);
const WARM_UP: Duration = Duration::from_millis(300);
struct MockGraphFile {
data: Vec<u8>,
}
impl MockGraphFile {
fn new(data: Vec<u8>) -> Self {
Self { data }
}
fn read_bytes(&mut self, offset: u64, buffer: &mut [u8]) -> NativeResult<()> {
let start = offset as usize;
let end = start + buffer.len();
if end > self.data.len() {
return Err(NativeBackendError::Io(std::io::Error::new(
std::io::ErrorKind::UnexpectedEof,
"Read past end of mock data",
)));
}
buffer.copy_from_slice(&self.data[start..end]);
Ok(())
}
}
fn create_test_cluster(neighbors: &[i64]) -> Vec<u8> {
let mut string_table = StringTable::new();
let edges: Vec<EdgeRecord> = neighbors
.iter()
.enumerate()
.map(|(idx, &id)| EdgeRecord {
id: idx as i64,
from_id: 1,
to_id: id,
edge_type: "TEST".to_string(),
data: serde_json::Value::Null,
flags: EdgeFlags::empty(),
})
.collect();
let cluster = EdgeCluster::create_from_edges(&edges, 1, Direction::Outgoing, &mut string_table)
.expect("Failed to create cluster");
cluster.serialize()
}
fn create_mock_data(cluster_count: usize) -> (Vec<u8>, Vec<(u64, u32)>) {
let mut data = Vec::new();
data.extend_from_slice(&[0u8; 1024]);
let mut offsets = Vec::with_capacity(cluster_count);
let mut current_offset = 1024u64;
for i in 0..cluster_count {
let neighbors = vec![((i + 1) as i64), ((i + 2) as i64)]; let cluster_data = create_test_cluster(&neighbors);
let cluster_size = cluster_data.len() as u32;
offsets.push((current_offset, cluster_size));
data.extend_from_slice(&cluster_data);
current_offset += cluster_size as u64;
}
(data, offsets)
}
fn bench_single_cluster_read(criterion: &mut Criterion) {
let mut group = criterion.benchmark_group("cluster_population/single");
group.measurement_time(MEASURE);
group.warm_up_time(WARM_UP);
let (data, offsets) = create_mock_data(1);
let mut graph_file = MockGraphFile::new(data);
group.bench_function("baseline", |b| {
b.iter(|| {
let total_size: u64 = offsets.iter().map(|(_, size)| *size as u64).sum();
let mut buffer = vec![0u8; total_size as usize];
let start_offset = offsets[0].0;
let _ =
black_box(graph_file.read_bytes(black_box(start_offset), black_box(&mut buffer)));
});
});
group.finish();
}
fn bench_multiple_cluster_read(criterion: &mut Criterion) {
let mut group = criterion.benchmark_group("cluster_population/multiple");
group.measurement_time(MEASURE);
group.warm_up_time(WARM_UP);
let cluster_counts = [10, 50, 100, 500];
for &count in &cluster_counts {
let (data, offsets) = create_mock_data(count);
let mut graph_file = MockGraphFile::new(data);
let total_bytes: u64 = offsets.iter().map(|(_, size)| *size as u64).sum();
group.throughput(Throughput::Bytes(total_bytes));
group.bench_with_input(BenchmarkId::from_parameter(count), &count, |b, &_count| {
b.iter(|| {
let total_size: u64 = offsets.iter().map(|(_, size)| *size as u64).sum();
let mut buffer = vec![0u8; total_size as usize];
let start_offset = offsets[0].0;
let _ = black_box(
graph_file.read_bytes(black_box(start_offset), black_box(&mut buffer)),
);
black_box(&buffer);
});
});
}
group.finish();
}
fn bench_cluster_read_throughput(criterion: &mut Criterion) {
let mut group = criterion.benchmark_group("cluster_population/throughput");
group.measurement_time(MEASURE);
group.warm_up_time(WARM_UP);
let cluster_counts = [10, 50, 100, 500];
for &count in &cluster_counts {
let (data, offsets) = create_mock_data(count);
let mut graph_file = MockGraphFile::new(data);
let total_bytes: u64 = offsets.iter().map(|(_, size)| *size as u64).sum();
group.throughput(Throughput::Bytes(total_bytes));
group.bench_with_input(
BenchmarkId::new(format!("{} clusters", count), total_bytes),
&total_bytes,
|b, &_bytes| {
b.iter(|| {
let total_size: u64 = offsets.iter().map(|(_, size)| *size as u64).sum();
let mut buffer = vec![0u8; total_size as usize];
let start_offset = offsets[0].0;
let _ = black_box(
graph_file.read_bytes(black_box(start_offset), black_box(&mut buffer)),
);
black_box(&buffer);
});
},
);
}
group.finish();
}
criterion_group!(
cluster_benches,
bench_single_cluster_read,
bench_multiple_cluster_read,
bench_cluster_read_throughput
);
criterion_main!(cluster_benches);