//! Thread-count scaling sweep for Whisper Tiny. //! Runs the JFK clip at n_threads = 1, 2, 4, 6, 8, 12, prints RTF table. //! Gated on the same env vars as jfk_bench. use std::env; use std::time::Instant; #[test] fn whisper_thread_count_sweep() { let Ok(model_path) = env::var("MAGNOTIA_WHISPER_TEST_MODEL") else { return }; let Ok(audio_path) = env::var("MAGNOTIA_WHISPER_TEST_AUDIO") else { return }; use whisper_rs::{FullParams, SamplingStrategy, WhisperContext, WhisperContextParameters}; let bytes = std::fs::read(&audio_path).expect("read wav"); let sample_rate = u32::from_le_bytes(bytes[24..28].try_into().unwrap()); let pcm = &bytes[44..]; let samples: Vec = pcm .chunks_exact(2) .map(|c| i16::from_le_bytes([c[0], c[1]]) as f32 / 32768.0) .collect(); let audio_secs = samples.len() as f64 / sample_rate as f64; eprintln!("[sweep] audio: {:.2}s @ {} Hz", audio_secs, sample_rate); let logical = num_cpus::get(); let physical = num_cpus::get_physical(); eprintln!("[sweep] CPU: physical={}, logical={}", physical, logical); let ctx = WhisperContext::new_with_params(&model_path, WhisperContextParameters::default()) .expect("model load"); // Warm-up pass at default to prime caches { let mut state = ctx.create_state().expect("state"); let mut params = FullParams::new(SamplingStrategy::Greedy { best_of: 1 }); params.set_language(Some("en")); params.set_n_threads(physical as i32); params.set_print_special(false); params.set_print_progress(false); params.set_print_realtime(false); state.full(params, &samples).expect("warmup"); } let mut targets: Vec = vec![1, 2, 4, physical as i32, logical as i32]; if logical >= 8 && !targets.contains(&8) { targets.push(8); } targets.sort(); targets.dedup(); eprintln!(""); eprintln!("=== n_threads scaling ==="); eprintln!("n_threads | xc_time | RTF | speedup_vs_1"); eprintln!("----------|---------|--------|-------------"); let mut baseline_dur: Option = None; for n in &targets { // Two runs, take the min (deeper of L2/L3 effects; we want best-case) let mut best = f64::MAX; for _ in 0..2 { let mut state = ctx.create_state().expect("state"); let mut params = FullParams::new(SamplingStrategy::Greedy { best_of: 1 }); params.set_language(Some("en")); params.set_n_threads(*n); params.set_print_special(false); params.set_print_progress(false); params.set_print_realtime(false); let t = Instant::now(); state.full(params, &samples).expect("transcribe"); let dur = t.elapsed().as_secs_f64(); if dur < best { best = dur; } } let rtf = best / audio_secs; let speedup = baseline_dur.map(|b| b / best).unwrap_or(1.0); if baseline_dur.is_none() { baseline_dur = Some(best); } eprintln!("{:>9} | {:>6.2}s | {:>6.3} | {:>6.2}x", n, best, rtf, speedup); } }