use candle_coreml::qwen::QwenModel;
use std::time::Instant;
const TEST_PROMPT: &str = "The quick brown fox jumps over the lazy";
const EXPECTED_TOKEN: i64 = 5562;
const PYTHON_BASELINE_TPS: f64 = 87.0;
fn main() -> Result<(), Box<dyn std::error::Error>> {
println!("ð Simple Performance Measurement");
println!("==================================");
#[cfg(target_os = "macos")]
{
let model_id = "anemll/anemll-Qwen-Qwen3-0.6B-LUT888-ctx512_0.3.4";
println!("ðĶ Loading model: {model_id}");
let model_dir = candle_coreml::ensure_model_downloaded(model_id, false)?;
let mut model = QwenModel::load_from_directory(&model_dir, None)?;
println!("â
Model loaded");
println!("\nðĨ Warming up...");
for _ in 0..3 {
let _ = model.generate_tokens_topk_temp(TEST_PROMPT, 1, 1.0, None);
}
println!("â
Warm up complete");
println!("\nð Performance Measurements:");
println!("{}", "=".repeat(50));
let start = Instant::now();
let iterations = 1000;
for _ in 0..iterations {
let _ = model.tokenize(TEST_PROMPT);
}
let tokenization_time = start.elapsed();
println!(
"ðĪ Tokenization: {:.2}Ξs per call ({} iterations)",
tokenization_time.as_micros() as f64 / iterations as f64,
iterations
);
println!("\nðŊ Single Token Generation (with prefill):");
let measurements = 5;
let mut times = Vec::new();
for i in 0..measurements {
let start = Instant::now();
let result = model.generate_tokens_topk_temp(TEST_PROMPT, 1, 1.0, None)?;
let duration = start.elapsed();
times.push(duration);
if let Some(token) = result.first() {
println!(
" Run {}: {:.0}ms (token: {})",
i + 1,
duration.as_millis(),
token
);
}
}
let avg_first_token = times.iter().sum::<std::time::Duration>() / times.len() as u32;
println!(" Average: {:.0}ms", avg_first_token.as_millis());
println!("\nð Multi-Token Generation:");
let token_counts = [5, 10, 25];
for &count in &token_counts {
let measurements = 3;
let mut times = Vec::new();
for i in 0..measurements {
let start = Instant::now();
let result = model.generate_tokens_topk_temp(TEST_PROMPT, count, 1.0, None)?;
let duration = start.elapsed();
times.push(duration);
let tps = count as f64 / duration.as_secs_f64();
let efficiency = (tps / PYTHON_BASELINE_TPS) * 100.0;
let expected_match = if count >= 2 && result.len() >= 2 {
if result[1] == EXPECTED_TOKEN {
"â
"
} else {
"â"
}
} else {
"?"
};
println!(
" {} tokens, run {}: {:.0}ms ({:.1} t/s, {:.1}% of Python) {}",
count,
i + 1,
duration.as_millis(),
tps,
efficiency,
expected_match
);
}
let avg_time = times.iter().sum::<std::time::Duration>() / times.len() as u32;
let avg_tps = count as f64 / avg_time.as_secs_f64();
let avg_efficiency = (avg_tps / PYTHON_BASELINE_TPS) * 100.0;
println!(
" {} tokens average: {:.0}ms ({:.1} t/s, {:.1}% of Python)",
count,
avg_time.as_millis(),
avg_tps,
avg_efficiency
);
}
println!("\nðŽ Component-Level Analysis:");
if let Ok(tokens) = model.tokenize(TEST_PROMPT) {
let device = candle_core::Device::Cpu;
let input_tensor =
candle_core::Tensor::from_vec(tokens.clone(), (1, tokens.len()), &device)?;
let start = Instant::now();
let _result = model.embeddings.forward(&[&input_tensor])?;
let embeddings_time = start.elapsed();
println!(
" Embeddings (full sequence): {:.2}ms",
embeddings_time.as_millis()
);
let single_token = candle_core::Tensor::from_vec(vec![tokens[0]], (1, 1), &device)?;
let start = Instant::now();
let _result = model.embeddings.forward(&[&single_token])?;
let single_embeddings_time = start.elapsed();
println!(
" Embeddings (single token): {:.2}ms",
single_embeddings_time.as_millis()
);
}
println!("\nð Summary:");
println!("{}", "=".repeat(50));
println!(
"âĒ Current performance: ~{:.1}% of Python baseline",
(11.0 / PYTHON_BASELINE_TPS) * 100.0
); println!("âĒ Bottleneck appears to be in CoreML inference pipeline");
println!("âĒ Tokenization is very fast (~few Ξs)");
println!("âĒ Each CoreML component call takes ~hundreds of ms");
println!("\nðŊ Optimization Opportunities:");
println!("1. CoreML model optimization (quantization, compilation settings)");
println!("2. Reduce tensor conversion overhead");
println!("3. Better memory management/pooling");
println!("4. Pipeline parallelization where possible");
}
#[cfg(not(target_os = "macos"))]
{
println!("â This benchmark requires macOS for CoreML support");
}
Ok(())
}