use anyhow::Result;
use clap::Parser;
use runtime::benchmark::{
print_comparison_table, print_single_results, BenchmarkComparison, BenchmarkConfig,
BenchmarkRunner, InferenceBackend, UniLLMBackend,
};
use runtime::ollama::OllamaRegistry;
#[cfg(feature = "benchmark")]
use runtime::benchmark::LlamaCppBackend;
#[derive(Parser, Debug)]
#[command(name = "benchmark_comparison")]
#[command(about = "Compare UniLLM and llama.cpp inference performance")]
struct Args {
#[arg(long, default_value = "tinyllama:latest")]
model: String,
#[arg(long, default_value = "3")]
warmup: usize,
#[arg(long, default_value = "10")]
iterations: usize,
#[arg(long, default_value = "50")]
max_tokens: usize,
#[arg(long)]
unillm_only: bool,
#[arg(long)]
#[cfg(feature = "benchmark")]
llama_cpp_only: bool,
}
const BENCHMARK_PROMPTS: &[&str] = &[
"The capital of France is",
"In machine learning, a neural network is",
"The quick brown fox",
];
#[tokio::main]
async fn main() -> Result<()> {
let args = Args::parse();
println!("╔══════════════════════════════════════════════════════════════╗");
println!("║ UniLLM vs llama.cpp Benchmark ║");
println!("╚══════════════════════════════════════════════════════════════╝\n");
println!("[1/4] Downloading model: {}", args.model);
let registry = OllamaRegistry::new()?;
let model_path = registry.pull(&args.model).await?;
println!(" Model path: {}", model_path.display());
let file_size = std::fs::metadata(&model_path)?.len();
println!(" Model size: {:.1} MB\n", file_size as f64 / (1024.0 * 1024.0));
let config = BenchmarkConfig {
model_path: model_path.clone(),
prompts: BENCHMARK_PROMPTS.iter().map(|s| s.to_string()).collect(),
max_new_tokens: args.max_tokens,
warmup_iterations: args.warmup,
measurement_iterations: args.iterations,
seed: 42,
};
let runner = BenchmarkRunner::new(config.clone());
#[cfg(feature = "benchmark")]
let run_llama_cpp = !args.unillm_only;
#[cfg(not(feature = "benchmark"))]
let run_llama_cpp = false;
let run_unillm = {
#[cfg(feature = "benchmark")]
{
!args.llama_cpp_only
}
#[cfg(not(feature = "benchmark"))]
{
true
}
};
#[cfg(feature = "benchmark")]
let llama_results = if run_llama_cpp {
println!("[2/4] Running llama.cpp benchmark...");
let mut llama_cpp = LlamaCppBackend::new();
let results = runner.run(&mut llama_cpp)?;
llama_cpp.unload();
std::thread::sleep(std::time::Duration::from_millis(500));
print_single_results(&results);
Some(results)
} else {
println!("[2/4] Skipping llama.cpp benchmark (--unillm-only)");
None
};
#[cfg(not(feature = "benchmark"))]
let llama_results: Option<runtime::benchmark::BenchmarkResults> = {
println!("[2/4] Skipping llama.cpp benchmark (benchmark feature not enabled)");
println!(" To enable: cargo run --release --bin benchmark_comparison -p runtime --features benchmark");
None
};
let unillm_results = if run_unillm {
println!("\n[3/4] Running UniLLM benchmark...");
let mut unillm = UniLLMBackend::new();
let results = runner.run(&mut unillm)?;
unillm.unload();
print_single_results(&results);
Some(results)
} else {
println!("\n[3/4] Skipping UniLLM benchmark (--llama-cpp-only)");
None
};
println!("\n[4/4] Generating comparison report...");
match (llama_results, unillm_results) {
(Some(llama), Some(unillm)) => {
let comparison = BenchmarkComparison {
llama_cpp: llama,
unillm,
};
print_comparison_table(&comparison);
}
(None, Some(unillm)) => {
println!("\n=== UniLLM Results Only ===");
println!("(llama.cpp comparison not available)\n");
print_single_results(&unillm);
print_fairness_notes();
}
(Some(llama), None) => {
println!("\n=== llama.cpp Results Only ===");
println!("(UniLLM comparison not available)\n");
print_single_results(&llama);
}
(None, None) => {
println!("\nNo benchmarks were run.");
}
}
println!("\n=== Benchmark Complete ===");
Ok(())
}
fn print_fairness_notes() {
println!("\n--- Notes ---");
println!("* UniLLM now uses KV caching for efficient autoregressive generation");
println!("* UniLLM runs on CPU only (no GPU acceleration yet)");
println!("* UniLLM dequantizes GGUF weights to F32 (higher memory, simpler compute)");
println!("* llama.cpp uses quantized inference with optimized SIMD kernels");
}