#![allow(clippy::collapsible_else_if)]
use anyhow::Result;
use clap::Parser;
use epserde::prelude::*;
use fallible_iterator::FallibleIterator;
use lender::*;
use sux::{
bits::BitFieldVec,
func::{shard_edge::*, *},
traits::TryIntoUnaligned,
utils::{LineLender, Sig, ToSig, ZstdLineLender},
};
#[cfg(target_pointer_width = "64")]
const INCR: usize = 0x9e3779b97f4a7c15;
#[cfg(not(target_pointer_width = "64"))]
const INCR: usize = 0x9e3779b9;
fn bench(n: usize, repeats: usize, mut f: impl FnMut()) {
let mut timings = Vec::with_capacity(repeats);
for _ in 0..repeats {
let start = std::time::Instant::now();
f();
timings.push(start.elapsed().as_nanos() as f64 / n as f64);
eprintln!("{} ns/key", timings.last().unwrap());
}
timings.sort_unstable_by(|a, b| a.total_cmp(b));
eprintln!(
"Min: {} Median: {} Max: {} Average: {}",
timings[0],
timings[timings.len() / 2],
timings.last().unwrap(),
timings.iter().sum::<f64>() / timings.len() as f64
);
}
#[derive(Parser, Debug)]
#[command(about = "Benchmarks VFunc with strings or 64-bit integers.", long_about = None, next_line_help = true, max_term_width = 100)]
struct Args {
n: usize,
func: String,
#[arg(short = 'f', long)]
filename: Option<String>,
#[arg(short, long, default_value = "5")]
repeats: usize,
#[arg(short, long)]
zstd: bool,
#[arg(long)]
sig64: bool,
#[arg(long)]
no_shards: bool,
#[arg(long)]
unaligned: bool,
#[arg(long, conflicts_with_all = ["sig64", "no_shards"])]
full_sigs: bool,
#[cfg(feature = "mwhc")]
#[arg(long, conflicts_with_all = ["sig64", "full_sigs"])]
mwhc: bool,
}
fn main() -> Result<()> {
env_logger::builder()
.filter_level(log::LevelFilter::Info)
.try_init()?;
let args = Args::parse();
#[cfg(feature = "mwhc")]
if args.mwhc {
return if args.no_shards {
main_with_types::<[u64; 2], Mwhc3NoShards>(args)
} else {
main_with_types::<[u64; 2], Mwhc3Shards>(args)
};
}
if args.no_shards {
if args.sig64 {
main_with_types::<[u64; 1], FuseLge3NoShards>(args)
} else {
main_with_types::<[u64; 2], FuseLge3NoShards>(args)
}
} else {
if args.full_sigs {
main_with_types::<[u64; 2], FuseLge3FullSigs>(args)
} else {
main_with_types::<[u64; 2], FuseLge3Shards>(args)
}
}
}
fn main_with_types<S: Sig + Send + Sync, E: ShardEdge<S, 3>>(args: Args) -> Result<()>
where
str: ToSig<S>,
usize: ToSig<S>,
VFunc<usize, BitFieldVec<Box<[usize]>>, S, E>: Deserialize,
VFunc<str, BitFieldVec<Box<[usize]>>, S, E>: Deserialize,
{
if let Some(filename) = args.filename {
let keys: Vec<_> = if args.zstd {
ZstdLineLender::from_path(filename)?
.map_into_iter(|x| Ok(x.to_owned()))
.take(args.n)
.collect()?
} else {
LineLender::from_path(filename)?
.map_into_iter(|x| Ok(x.to_owned()))
.take(args.n)
.collect()?
};
let func = unsafe { VFunc::<str, BitFieldVec<Box<[usize]>>, S, E>::load_full(&args.func) }?;
if args.unaligned {
let func = func.try_into_unaligned().unwrap();
bench(args.n, args.repeats, || {
for key in &keys {
std::hint::black_box(func.get(key.as_str()));
}
});
} else {
bench(args.n, args.repeats, || {
for key in &keys {
std::hint::black_box(func.get(key.as_str()));
}
});
}
} else {
let func =
unsafe { VFunc::<usize, BitFieldVec<Box<[usize]>>, S, E>::load_full(&args.func) }?;
if args.unaligned {
let func = func.try_into_unaligned().unwrap();
bench(args.n, args.repeats, || {
let mut key: usize = 0;
for _ in 0..args.n {
key = key.wrapping_add(INCR);
std::hint::black_box(func.get(key));
}
});
} else {
bench(args.n, args.repeats, || {
let mut key: usize = 0;
for _ in 0..args.n {
key = key.wrapping_add(INCR);
std::hint::black_box(func.get(key));
}
});
}
}
Ok(())
}