use pgm_index::PGMIndex;
use rand::{Rng, SeedableRng, rngs::StdRng};
use rayon::prelude::*;
use std::time::Instant;
fn main() {
println!("=== PGM-Index Simple Benchmark ===\n");
pgm_index::init_rayon_min_threads();
println!("Rayon threads: {}", rayon::current_num_threads());
const N: usize = 1_000_000_000;
const QUERY_COUNT: usize = 100_000;
let mut rng = StdRng::seed_from_u64(42);
println!("Creating dataset with {} elements...", N);
let data: Vec<u64> = (0..N as u64).collect();
let mut queries = Vec::with_capacity(QUERY_COUNT);
for _ in 0..QUERY_COUNT {
queries.push(rng.gen_range(0..N as u64));
}
for &epsilon in &[16usize, 32, 64, 128] {
test_epsilon(epsilon, &data, &queries);
}
println!(
"\nTip: smaller ε ⇒ more segments (faster queries, higher memory); \
larger ε ⇒ fewer segments (slower, lower memory)."
);
}
fn test_epsilon(epsilon: usize, data: &[u64], queries: &[u64]) {
println!("=== PGM-Index (ε = {}) ===", epsilon);
let build_start = Instant::now();
let index = PGMIndex::new(data.to_vec(), epsilon);
let build_time = build_start.elapsed();
println!("Build time: {:?}", build_time);
println!("Segments: {}", index.segment_count());
println!("Avg segment size: {:.1}", index.avg_segment_size());
println!(
"Memory usage: {:.2} MB",
index.memory_usage() as f64 / 1024.0 / 1024.0
);
println!(
"Memory overhead: {:.2}%",
(index.memory_usage() as f64 / (data.len() * 8) as f64 - 1.0) * 100.0
);
let single_start = Instant::now();
let mut hits = 0usize;
for &q in &queries[0..queries.len().min(10)] {
if index.get(q).is_some() {
hits += 1;
}
}
let single_time = single_start.elapsed();
println!("Single query time ({} hits / 10): {:?}", hits, single_time);
let batch_start = Instant::now();
let mut hits = 0usize;
for &q in queries {
if index.get(q).is_some() {
hits += 1;
}
}
let batch_time = batch_start.elapsed();
let ns_per_query = batch_time.as_nanos() as f64 / (queries.len() as f64);
println!("Batch query time: {:?}", batch_time);
println!(
"Batch throughput: {:.0} queries/sec",
(queries.len() as f64) / batch_time.as_secs_f64()
);
println!("Batch average: {:.1} ns/query", ns_per_query);
println!("Hits: {}/{}", hits, queries.len());
let par_start = Instant::now();
let par_hits = queries
.par_iter()
.filter(|&&q| index.get(q).is_some())
.count();
let par_time = par_start.elapsed();
let par_ns_per_query = par_time.as_nanos() as f64 / (queries.len() as f64);
println!("Parallel batch time: {:?}", par_time);
println!(
"Parallel throughput: {:.0} queries/sec",
(queries.len() as f64) / par_time.as_secs_f64()
);
println!("Parallel average: {:.1} ns/query", par_ns_per_query);
println!("Parallel hits: {}/{}", par_hits, queries.len());
let test_keys = vec![data[0], data[data.len() / 2], data[data.len() - 1]];
let start = Instant::now();
for &key in &test_keys {
let _ = index.get(key);
}
let query_time = start.elapsed();
println!("Query time (3 edge keys): {:?}", query_time);
}