use std::path::PathBuf;
#[derive(Debug, Clone)]
pub struct BenchmarkConfig {
pub model_path: PathBuf,
pub prompts: Vec<String>,
pub max_new_tokens: usize,
pub warmup_iterations: usize,
pub measurement_iterations: usize,
pub seed: u64,
}
impl Default for BenchmarkConfig {
fn default() -> Self {
Self {
model_path: PathBuf::new(),
prompts: vec![
"The capital of France is".to_string(),
"In machine learning, a neural network is".to_string(),
"The quick brown fox".to_string(),
],
max_new_tokens: 50,
warmup_iterations: 3,
measurement_iterations: 10,
seed: 42,
}
}
}
#[derive(Debug, Clone)]
pub struct InferenceMetrics {
pub load_time_ms: f64,
pub time_to_first_token_ms: f64,
pub total_inference_time_ms: f64,
pub tokens_generated: usize,
pub tokens_per_second: f64,
pub prompt_tokens: usize,
pub memory_before_load_bytes: u64,
pub memory_after_load_bytes: u64,
pub peak_memory_bytes: u64,
}
#[derive(Debug, Clone)]
pub struct GenerationResult {
pub output_text: String,
pub tokens_generated: usize,
pub prompt_tokens: usize,
pub time_to_first_token_ms: f64,
pub total_time_ms: f64,
}
#[derive(Debug, Clone)]
pub struct BenchmarkResults {
pub backend_name: String,
pub runs: Vec<InferenceMetrics>,
pub load_time_ms: f64,
pub avg_ttft_ms: f64,
pub avg_tokens_per_sec: f64,
pub p50_tokens_per_sec: f64,
pub p99_tokens_per_sec: f64,
pub avg_memory_mb: f64,
pub peak_memory_mb: f64,
}
impl BenchmarkResults {
pub fn aggregate(
backend_name: &str,
runs: Vec<InferenceMetrics>,
load_time_ms: f64,
peak_memory_bytes: u64,
) -> Self {
let n = runs.len() as f64;
let avg_ttft_ms = runs.iter().map(|r| r.time_to_first_token_ms).sum::<f64>() / n;
let avg_tokens_per_sec = runs.iter().map(|r| r.tokens_per_second).sum::<f64>() / n;
let avg_memory_bytes =
runs.iter().map(|r| r.memory_after_load_bytes).sum::<u64>() as f64 / n;
let mut tps_values: Vec<f64> = runs.iter().map(|r| r.tokens_per_second).collect();
tps_values.sort_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal));
let p50_idx = (tps_values.len() as f64 * 0.5) as usize;
let p99_idx = (tps_values.len() as f64 * 0.99) as usize;
let p50_tokens_per_sec = tps_values.get(p50_idx).copied().unwrap_or(0.0);
let p99_tokens_per_sec = tps_values
.get(p99_idx.min(tps_values.len() - 1))
.copied()
.unwrap_or(0.0);
Self {
backend_name: backend_name.to_string(),
runs,
load_time_ms,
avg_ttft_ms,
avg_tokens_per_sec,
p50_tokens_per_sec,
p99_tokens_per_sec,
avg_memory_mb: avg_memory_bytes / (1024.0 * 1024.0),
peak_memory_mb: peak_memory_bytes as f64 / (1024.0 * 1024.0),
}
}
}
#[derive(Debug)]
pub struct BenchmarkComparison {
pub llama_cpp: BenchmarkResults,
pub unillm: BenchmarkResults,
}
impl BenchmarkComparison {
pub fn diff_percent(baseline: f64, comparison: f64) -> f64 {
if baseline == 0.0 {
return 0.0;
}
((comparison - baseline) / baseline) * 100.0
}
pub fn format_diff(baseline: f64, comparison: f64) -> String {
let diff = Self::diff_percent(baseline, comparison);
if diff >= 0.0 {
format!("+{:.0}%", diff)
} else {
format!("{:.0}%", diff)
}
}
}