use memra_engine::Engine;
use memra_engine::hybrid::HybridModel;
use memra_engine::forward::argmax;
use memra_gguf::GgufFile;
fn pct(sorted: &[f64], p: f64) -> f64 {
let i = ((sorted.len() as f64 - 1.0) * p).round() as usize;
sorted[i]
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
let path = std::env::args().nth(1).expect("usage: verify-mscale <model> [depth] [reps] [ms]");
let depth: usize = std::env::args().nth(2).and_then(|s| s.parse().ok()).unwrap_or(512);
let reps: usize = std::env::args().nth(3).and_then(|s| s.parse().ok()).unwrap_or(40);
let ms: Vec<usize> = std::env::args().nth(4)
.unwrap_or_else(|| "1,2,3,4,6".to_string())
.split(',').filter_map(|s| s.parse().ok()).collect();
let max_m = ms.iter().copied().max().unwrap_or(6);
let e = Engine::new(0)?;
let g = GgufFile::open(&path)?;
let model = HybridModel::load(&e, &g)?;
let n_embd = model.cfg.n_embd as usize;
let prompt: Vec<u32> = (0..depth).map(|i| (100 + (i * 7) % 900) as u32).collect();
let mut cache = memra_engine::pp::new_cache(&e, &model.cfg, depth + max_m + 32)?;
let t_prime = std::time::Instant::now();
let mut last_logits: Vec<f32> = if depth >= memra_engine::hybrid_forward::PRIME_MIN_T {
let (l, _h, _hiddens) = model.prime_cache(&e, &prompt, &mut cache, 0)?;
l
} else {
let mut l = Vec::new();
for &t in &prompt { l = model.decode_step(&e, t, &mut cache)?; }
l
};
e.stream().synchronize()?;
println!("primed depth={} in {:.2}s", cache.pos, t_prime.elapsed().as_secs_f64());
let snap = cache.snapshot(&e)?;
let pos0 = cache.pos;
let mut gold: Vec<u32> = Vec::with_capacity(max_m);
let mut ll = last_logits.clone();
for _ in 0..max_m {
let nx = argmax(&ll) as u32;
gold.push(nx);
ll = model.decode_step(&e, nx, &mut cache)?;
}
cache.rollback(&e, &snap, 0)?;
let _ = &mut last_logits;
println!("verify tokens (greedy continuation): {gold:?}");
let embd_gpu = model.embd_gpu.get_or_init(|| {
e.upload_u8(&model.embd.raw).expect("embed table upload")
});
let (embd_qt, embd_rb) = model.embd.qt_and_row_bytes(n_embd);
let profile = std::env::var("MEMRA_MSCALE_PROFILE").is_ok();
if std::env::var("MEMRA_MSCALE_NOEAGER").is_err() {
for _ in 0..3 { let _ = model.decode_step(&e, gold[0], &mut cache)?; cache.rollback(&e, &snap, 0)?; }
e.stream().synchronize()?;
let mut ts: Vec<f64> = Vec::with_capacity(reps);
for _ in 0..reps {
e.stream().synchronize()?;
let t0 = std::time::Instant::now();
let _ = model.decode_step(&e, gold[0], &mut cache)?;
e.stream().synchronize()?;
ts.push(t0.elapsed().as_secs_f64() * 1e6);
cache.rollback(&e, &snap, 0)?;
}
ts.sort_by(|a, b| a.partial_cmp(b).unwrap());
println!("eager decode_step @d{depth}: median {:8.1} us p10 {:8.1} p90 {:8.1}",
pct(&ts, 0.5), pct(&ts, 0.1), pct(&ts, 0.9));
}
let interleave = std::env::var("MEMRA_MSCALE_INTERLEAVE").as_deref() == Ok("1");
let mut samples: Vec<Vec<f64>> = ms.iter()
.map(|_| Vec::with_capacity(reps))
.collect();
if interleave {
for &m in &ms {
let toks = &gold[0..m];
for _ in 0..3 {
let _ = model.decode_step_t_h_emb_dev(&e, toks, pos0, &mut cache,
Some((embd_gpu, embd_qt, embd_rb)))?;
cache.rollback(&e, &snap, 0)?;
}
}
e.stream().synchronize()?;
if profile { unsafe { cudarc::driver::sys::cuProfilerStart().result()?; } }
println!("measurement order: alternating forward/reverse by repetition");
for rep in 0..reps {
for ordinal in 0..ms.len() {
let i = if rep % 2 == 0 { ordinal } else { ms.len() - 1 - ordinal };
let m = ms[i];
let toks = &gold[0..m];
e.stream().synchronize()?;
let t0 = std::time::Instant::now();
let _ = model.decode_step_t_h_emb_dev(&e, toks, pos0, &mut cache,
Some((embd_gpu, embd_qt, embd_rb)))?;
e.stream().synchronize()?;
samples[i].push(t0.elapsed().as_secs_f64() * 1e6);
cache.rollback(&e, &snap, 0)?;
}
}
if profile { unsafe { cudarc::driver::sys::cuProfilerStop().result()?; } }
} else {
for (i, &m) in ms.iter().enumerate() {
let toks = &gold[0..m];
for _ in 0..3 {
let _ = model.decode_step_t_h_emb_dev(&e, toks, pos0, &mut cache,
Some((embd_gpu, embd_qt, embd_rb)))?;
cache.rollback(&e, &snap, 0)?;
}
e.stream().synchronize()?;
if profile { unsafe { cudarc::driver::sys::cuProfilerStart().result()?; } }
for _ in 0..reps {
e.stream().synchronize()?;
let t0 = std::time::Instant::now();
let _ = model.decode_step_t_h_emb_dev(&e, toks, pos0, &mut cache,
Some((embd_gpu, embd_qt, embd_rb)))?;
e.stream().synchronize()?;
samples[i].push(t0.elapsed().as_secs_f64() * 1e6);
cache.rollback(&e, &snap, 0)?;
}
if profile { unsafe { cudarc::driver::sys::cuProfilerStop().result()?; } }
}
}
let mut med1 = 0.0f64;
for (i, &m) in ms.iter().enumerate() {
let ts = &mut samples[i];
ts.sort_by(|a, b| a.partial_cmp(b).unwrap());
let med = pct(ts, 0.5);
if i == 0 { med1 = med; }
println!("verify m={m} @d{depth}: median {:8.1} us p10 {:8.1} p90 {:8.1} | x{:.3} vs m={} | {:7.1} us/tok",
med, pct(ts, 0.1), pct(ts, 0.9), med / med1, ms[0], med / m as f64);
}
Ok(())
}