use anyhow::Result;
use inferencelayer::cuda_llm::CudaQwen3;
fn main() -> Result<()> {
let a: Vec<String> = std::env::args().collect();
if a.len() < 2 {
eprintln!("usage: cuda-llm-bench <model-dir> [prompt-tokens=16] [ngen=128]");
std::process::exit(2);
}
let dir = std::path::PathBuf::from(&a[1]);
let plen: usize = a.get(2).and_then(|s| s.parse().ok()).unwrap_or(16);
let ngen: usize = a.get(3).and_then(|s| s.parse().ok()).unwrap_or(128);
let prompt: Vec<u32> = match a.get(4) {
Some(f) => std::fs::read_to_string(f)?
.trim()
.split(',')
.filter(|s| !s.is_empty())
.map(|s| s.trim().parse::<u32>().unwrap())
.collect(),
None => (0..plen).map(|i| (1000 + i as u32) % 1000 + 100).collect(),
};
let plen = prompt.len();
let t0 = std::time::Instant::now();
let mut m = CudaQwen3::load(&dir, plen + ngen + 16)?;
m.sync()?;
println!("loaded in {:.1}s cfg={:?}", t0.elapsed().as_secs_f64(), m.cfg());
let tp = std::time::Instant::now();
for (i, &t) in prompt.iter().enumerate() {
m.seed(t, i)?;
m.step(0, 0)?;
}
m.sync()?;
if std::env::var("VERIFY_GEMM").is_ok() && m.cfg().num_experts == 0 {
m.init_batch(1, plen + ngen + 16)?;
m.verify_dequant()?;
let (d, scale) = m.verify_gemm()?;
println!("== GEMM vs GEMV: max|diff| = {d:.4}, max|value| = {scale:.4} (rel {:.4}) => {}",
d / scale.max(1e-6), if d / scale.max(1e-6) < 0.05 { "OK" } else { "WRONG" });
return Ok(());
}
if std::env::var("VERIFY_ARM").is_ok() && m.cfg().num_experts == 0 {
let n = 24usize;
let seed = prompt[0];
m.seed(seed, 0)?;
let mut single = Vec::new();
for _ in 0..n {
m.step(0, 0)?;
m.sync()?;
single.push(m.last_token()?);
}
let mut run = |eng: &mut inferencelayer::cuda_llm::CudaQwen3, q4: bool|
-> anyhow::Result<(Vec<u32>, Vec<(u32, f32, f32)>)> {
eng.set_q4_arm(q4);
eng.seed_batch(&[seed, seed], 0)?;
let mut marg = Vec::new();
for _ in 0..n {
eng.step_batch()?;
eng.sync()?;
let lg = eng.batch_logits(0)?;
let mut best = (0usize, f32::MIN);
let mut second = f32::MIN;
for (i, &v) in lg.iter().enumerate() {
if v > best.1 { second = best.1; best = (i, v); }
else if v > second { second = v; }
}
marg.push((best.0 as u32, best.1, best.1 - second));
}
let toks = eng.batch_tokens(0, 1, n + 1)?;
Ok((toks, marg))
};
m.init_batch(2, n + 8)?;
let (bf16, mb) = run(&mut m, false)?;
let (q4, mq) = run(&mut m, true)?;
if let Some(k) = (0..n.min(mb.len()).min(mq.len())).find(|&i| mb[i].0 != mq[i].0) {
println!("== FIRST DIVERGENCE at step {k}");
println!(" bf16 arm: top1 id {} logit {:.5}, margin over top2 {:.5}", mb[k].0, mb[k].1, mb[k].2);
println!(" q4 arm: top1 id {} logit {:.5}, margin over top2 {:.5}", mq[k].0, mq[k].1, mq[k].2);
let verdict = if mb[k].2 < 0.05 || mq[k].2 < 0.05 { "NEAR-TIE — rounding, not a defect" }
else { "WIDE MARGIN — real defect in the arm" };
println!(" => {verdict}");
} else {
println!("== no divergence in the sampled ids");
}
let agree = |a: &[u32], b: &[u32]| a.iter().zip(b).take_while(|(x, y)| x == y).count();
println!("== single-stream (reference): {:?}", &single[..12.min(single.len())]);
println!("== batched bf16 mirrors: {:?}", &bf16[..12.min(bf16.len())]);
println!("== batched q4 arm: {:?}", &q4[..12.min(q4.len())]);
println!(" bf16 agrees with reference for {}/{} tokens", agree(&single, &bf16), n);
println!(" q4 agrees with reference for {}/{} tokens", agree(&single, &q4), n);
return Ok(());
}
if std::env::var("VERIFY_DET").is_ok() && m.cfg().num_experts == 0 {
m.init_batch(2, plen + 64)?;
let a = m.prefill_logits(&prompt)?;
let b = m.prefill_logits(&prompt)?;
let c = m.prefill_logits(&prompt)?;
let bitsame = |x: &[f32], y: &[f32]| x.iter().zip(y).all(|(p, q)| p.to_bits() == q.to_bits());
let ndiff = a.iter().zip(&b).filter(|(p, q)| p.to_bits() != q.to_bits()).count();
println!("== PREFILL DETERMINISM over {} logits, 3 runs:", a.len());
println!(" run1 vs run2: {}", if bitsame(&a, &b) { "BIT-IDENTICAL" } else { "DIFFERS" });
println!(" run1 vs run3: {}", if bitsame(&a, &c) { "BIT-IDENTICAL" } else { "DIFFERS" });
if ndiff > 0 { println!(" {ndiff} logits differ"); }
let mut runs = Vec::new();
for _ in 0..3 {
m.seed_batch(&[prompt[0], prompt[0]], 0)?;
for _ in 0..24 { m.step_batch()?; }
m.sync()?;
runs.push(m.batch_tokens(0, 1, 25)?);
}
let same = runs[0] == runs[1] && runs[0] == runs[2];
println!("== DECODE DETERMINISM, 3 runs x 24 tokens: {}",
if same { "IDENTICAL" } else { "DIFFERS" });
println!(" {:?}", &runs[0][..8.min(runs[0].len())]);
return Ok(());
}
if std::env::var("VERIFY_SEQ").is_ok() && m.cfg().num_experts == 0 {
let slots: usize = std::env::var("SLOTS").ok().and_then(|v| v.parse().ok()).unwrap_or(2);
let bmax2: usize = std::env::var("BMAX").ok().and_then(|v| v.parse().ok()).unwrap_or(plen + 64);
m.init_batch(slots, bmax2)?;
m.prefill_slot(&prompt, Some(0))?; m.sync()?;
let first = m.last_token()?;
m.seed_seq_head(first, plen)?;
m.step(0, 0)?;
m.sync()?;
let lb = m.seq_logits()?;
m.set_width(1)?;
m.occupy_slot(0, first, plen)?;
m.step_batch()?;
m.sync()?;
let la = m.batch_logits(0)?;
let top = |l: &[f32]| {
let mut b = (0usize, f32::MIN); let mut s2 = f32::MIN;
for (i, &v) in l.iter().enumerate() {
if v > b.1 { s2 = b.1; b = (i, v); } else if v > s2 { s2 = v; }
}
(b.0, b.1, b.1 - s2)
};
let (ai, av, am) = top(&la);
let (bi, bv, bm) = top(&lb);
let mut md = 0f32; let mut mi = 0usize;
for i in 0..la.len().min(lb.len()) {
let d = (la[i] - lb[i]).abs();
if d > md { md = d; mi = i; }
}
println!("== SEQ vs BATCH one step from identical state (input={first}, pos={plen})");
println!(" batch: top1 id {ai} logit {av:.4} margin {am:.4}");
println!(" seq: top1 id {bi} logit {bv:.4} margin {bm:.4}");
println!(" max|logit diff| = {md:.4} at id {mi} (batch {:.4} vs seq {:.4})", la[mi], lb[mi]);
m.seed_seq_head(first, plen)?;
m.capture()?;
m.seed_seq_head(first, plen)?;
m.run_graph(1)?;
m.sync()?;
let lg = m.seq_logits()?;
let (gi, gv, gm) = top(&lg);
let mut md2 = 0f32; let mut mi2 = 0usize;
for i in 0..lb.len().min(lg.len()) {
let d = (lb[i] - lg[i]).abs();
if d > md2 { md2 = d; mi2 = i; }
}
println!(" graph-replay: top1 id {gi} logit {gv:.4} margin {gm:.4}");
println!(" max|eager-seq vs replay diff| = {md2:.4} at id {mi2}");
m.prefill_slot_graphed(&prompt, 0)?; m.sync()?;
m.prefill_slot_graphed(&prompt, 0)?; m.sync()?;
let f2 = m.last_token()?;
println!(" graphed-prefill leftover: d_token={f2} (eager gave {first})");
m.occupy_slot_async(0, plen)?;
let firsts = m.first_tokens()?;
m.set_slot_token_async(0, firsts[0])?;
m.run_graph(1)?;
m.sync()?;
let t3 = m.last_token()?;
println!(" seq step after graphed prefill + slot_ctl/first_tokens: token={t3} (want 11)");
m.set_width(1)?;
m.capture_batch()?;
m.prefill_slot_graphed(&prompt, 0)?;
m.sync()?;
let mut toks = Vec::new();
toks.push(m.last_token()?);
for _ in 0..8 {
m.run_graph(1)?;
m.sync()?;
toks.push(m.last_token()?);
}
println!(" with batch graph resident, seq loop: {toks:?} (expect [12095, 11/13, ...coherent])");
return Ok(());
}
if std::env::var("Q4GEMM").is_ok() && m.cfg().num_experts == 0 {
m.init_batch(1, 64)?;
for t in [8usize, 16, 32, 64, 128, 256, 2000] {
println!("--- M = {t}");
m.verify_q4_gemm(t, 200)?;
}
return Ok(());
}
if std::env::var("GEMM_BENCH").is_ok() && m.cfg().num_experts == 0 {
m.init_batch(1, 64)?;
m.bench_gemm(plen.max(2000), 100)?;
return Ok(());
}
if std::env::var("VERIFY_FA").is_ok() && m.cfg().num_experts == 0 {
m.init_batch(1, plen + 32)?;
m.set_fa(false);
let a = m.prefill_logits(&prompt)?;
m.set_fa(true);
let b = m.prefill_logits(&prompt)?;
let (mut d, mut sc) = (0f32, 0f32);
for (x, y) in a.iter().zip(b.iter()) {
d = d.max((x - y).abs());
sc = sc.max(x.abs());
}
let am = |v: &[f32]| v.iter().enumerate().max_by(|p, q| p.1.total_cmp(q.1)).unwrap().0;
let rel = d / sc.max(1e-6);
println!("== FLASH vs SCORE-MATRIX: max|diff| = {d:.4}, max|logit| = {sc:.4} (rel {rel:.5})");
println!(" argmax gemm = {}, argmax flash = {} => {}", am(&a), am(&b),
if am(&a) == am(&b) && rel < 0.02 { "OK" } else { "WRONG" });
return Ok(());
}
if std::env::var("PREFILL").is_ok() && m.cfg().num_experts == 0 {
m.init_batch(1, plen + ngen + 16)?;
m.prefill(&prompt)?; m.sync()?;
let tp2 = std::time::Instant::now();
m.prefill(&prompt)?;
m.sync()?;
let dt = tp2.elapsed().as_secs_f64();
println!("== BATCHED PREFILL: {plen} tokens in {dt:.4}s = {:.0} tok/s", plen as f64 / dt);
m.capture_prefill(&prompt)?;
m.run_prefill_graph(&prompt)?;
m.sync()?;
let tg = std::time::Instant::now();
m.run_prefill_graph(&prompt)?;
m.sync()?;
let dg = tg.elapsed().as_secs_f64();
println!("== GRAPH PREFILL: {plen} tokens in {dg:.4}s = {:.0} tok/s", plen as f64 / dg);
let pf_tok = m.last_token()?;
let mut seq_tok = 0u32;
for (i, &t) in prompt.iter().enumerate() {
m.seed(t, i)?;
m.step(0, 0)?;
if i + 1 == prompt.len() { m.sync()?; seq_tok = m.last_token()?; }
}
println!(" prefill token = {pf_tok}, sequential token = {seq_tok} => {}",
if pf_tok == seq_tok { "MATCH" } else { "MISMATCH (prefill math wrong)" });
}
let prefill_end = plen;
println!("prefill({plen} tok, sequential) {:.2}s", tp.elapsed().as_secs_f64());
let batch_only = std::env::var("BATCH_ONLY").is_ok();
m.seed(m.tokens(prefill_end - 1, prefill_end)?[0], prefill_end)?;
m.step(0, 0)?;
m.sync()?;
let te = std::time::Instant::now();
if !batch_only { for _ in 0..ngen { m.step(0, 0)?; } }
m.sync()?;
let eager = ngen as f64 / te.elapsed().as_secs_f64();
m.seed(m.tokens(prefill_end - 1, prefill_end)?[0], prefill_end)?;
m.capture()?;
m.run_graph(2)?;
m.sync()?;
let start = prefill_end;
m.seed(m.tokens(prefill_end - 1, prefill_end)?[0], start)?;
let tg = std::time::Instant::now();
m.run_graph(if batch_only { 1 } else { ngen })?;
m.sync()?;
let dt = tg.elapsed().as_secs_f64();
let ids = m.tokens(start, start + ngen)?;
println!("first 24 generated ids: {:?}", &ids[..ids.len().min(24)]);
if !batch_only {
std::fs::write("/root/ours_ids.txt",
ids.iter().map(|t| t.to_string()).collect::<Vec<_>>().join(","))?;
}
println!("== EAGER decode: {eager:.1} tok/s");
println!("\n== DECODE: {ngen} tokens in {dt:.3}s = {:.1} tok/s", ngen as f64 / dt);
println!(" vLLM reference on the same H100 (Qwen3-30B-A3B BF16): 155.9 tok/s");
if let Ok(ms) = std::env::var("BATCH") {
let bm: usize = ms.parse().unwrap_or(4);
if m.cfg().num_experts != 0 {
println!("\n== CONCURRENCY: skipped (batched path is the dense arm)");
return Ok(());
}
let bmax = std::env::var("BMAX").ok().and_then(|v| v.parse().ok()).unwrap_or(256usize);
m.init_batch(bm, bmax)?;
let seeds: Vec<u32> = (0..bm).map(|i| prompt[i % prompt.len()]).collect();
m.seed_batch(&seeds, 0)?;
for _ in 0..8 { m.step_batch()?; }
m.sync()?;
m.capture_batch()?;
m.run_graph_batch(2)?;
m.sync()?;
let tb = std::time::Instant::now();
if std::env::var("SYNC_EACH").is_ok() {
for _ in 0..ngen {
m.run_graph_batch(1)?;
m.sync()?;
let _ = m.batch_last_tokens()?;
}
} else {
m.run_graph_batch(ngen)?;
m.sync()?;
}
let dt2 = tb.elapsed().as_secs_f64();
println!("\n== CONCURRENCY M={bm}: {:.1} tok/s per seq, AGGREGATE {:.1} tok/s",
ngen as f64 / dt2, (bm * ngen) as f64 / dt2);
println!(" seq0 ids[8..20]: {:?}", m.batch_tokens(0, 8, 20)?);
}
Ok(())
}