use crate::Result;
use crate::binary::{StringTable, StringTableBuilder, compute_blake3};
use crate::streaming::XorPatcher;
use console::style;
use std::path::Path;
use std::time::Instant;
#[derive(Debug)]
pub struct BenchmarkCommand;
#[derive(Debug, Default)]
pub struct BenchmarkResults {
pub string_lookup_ns: f64,
pub xor_compute_us: f64,
pub hash_throughput: f64,
}
impl BenchmarkCommand {
pub fn execute(iterations: usize) -> Result<BenchmarkResults> {
println!(
"{} Running DX Binary Dawn Benchmarks ({} iterations)",
style("⚡").bold(),
iterations
);
println!();
let mut results = BenchmarkResults::default();
println!(" {} String Table Lookup...", style("▸").cyan());
results.string_lookup_ns = Self::bench_string_table(iterations);
println!(" Lookup: {:.2} ns/op", results.string_lookup_ns);
println!(" {} XOR Patcher...", style("▸").cyan());
results.xor_compute_us = Self::bench_xor_patcher(iterations);
println!(" Compute: {:.2} µs/op", results.xor_compute_us);
println!(" {} Blake3 Checksum...", style("▸").cyan());
results.hash_throughput = Self::bench_blake3(iterations);
println!(
" Throughput: {:.2} MB/s",
results.hash_throughput / 1_000_000.0
);
println!();
println!("{} Benchmarks complete!", style("✓").green().bold());
Ok(results)
}
fn bench_string_table(iterations: usize) -> f64 {
let mut builder = StringTableBuilder::new();
let strings: Vec<_> = (0..1000).map(|i| format!("string_{}", i)).collect();
let ids: Vec<_> = strings.iter().map(|s| builder.intern(s)).collect();
let table_bytes = builder.build();
let table = StringTable::from_bytes(&table_bytes).unwrap();
let start = Instant::now();
for _ in 0..iterations {
for &id in &ids {
let _ = table.get(id);
}
}
let elapsed = start.elapsed();
let total_ops = ids.len() * iterations;
elapsed.as_nanos() as f64 / total_ops as f64
}
fn bench_xor_patcher(iterations: usize) -> f64 {
let old = vec![0u8; 4096];
let mut new = old.clone();
for i in (0..new.len()).step_by(100) {
new[i] = 1;
}
let patcher = XorPatcher::new(64);
let start = Instant::now();
for _ in 0..iterations {
let _ = patcher.compute(&old, &new);
}
let elapsed = start.elapsed();
elapsed.as_micros() as f64 / iterations as f64
}
fn bench_blake3(iterations: usize) -> f64 {
let data = vec![0u8; 65536];
let start = Instant::now();
for _ in 0..iterations {
let _ = compute_blake3(&data);
}
let elapsed = start.elapsed();
let total_bytes = data.len() * iterations;
total_bytes as f64 / elapsed.as_secs_f64()
}
pub fn bench_file(path: &Path) -> Result<()> {
println!(
"{} Benchmarking file: {}",
style("⚡").bold(),
path.display()
);
let data = std::fs::read(path)?;
let size = data.len();
let start = Instant::now();
let hash = compute_blake3(&data);
let hash_time = start.elapsed();
println!();
println!(" File size: {} bytes", size);
println!(" Hash: {:?}", hash_time);
println!(" Blake3: {:02x?}", hash);
Ok(())
}
}