use memra_engine::Engine;
use memra_engine::cache::Cache;
use memra_engine::forward::argmax;
use memra_engine::hybrid::HybridModel;
use memra_gguf::GgufFile;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut args = std::env::args().skip(1);
let path = args
.next()
.expect("usage: prime-batch-gate <model.gguf> [--batch N]");
let rest: Vec<String> = args.collect();
let opt_usize = |name: &str, default: usize| {
rest.iter()
.position(|a| a == name)
.and_then(|i| rest.get(i + 1))
.and_then(|v| v.parse().ok())
.unwrap_or(default)
};
let b = opt_usize("--batch", 3);
let plen = opt_usize("--plen", 24);
let steps = opt_usize("--steps", 16);
let exact = rest.iter().any(|a| a == "--exact");
let require_pp_split = rest.iter().any(|a| a == "--require-pp-split");
let e = Engine::new(0)?;
let g = GgufFile::open(&path)?;
let model = HybridModel::load_without_mtp(&e, &g)?;
println!(
"loaded {} ({} layers); batch={b}",
g.arch().unwrap_or("?"),
model.layers.len()
);
let prompts: Vec<Vec<u32>> = (0..b)
.map(|i| {
(0..plen as u32 + i as u32 * 17)
.map(|j| 55 + i as u32 * 97 + j * 31)
.collect()
})
.collect();
let ctx = prompts.iter().map(Vec::len).max().unwrap_or(0) + steps + 32;
let mut fails = 0usize;
let bit_diff = |a: &[f32], b: &[f32]| -> usize {
a.iter()
.zip(b)
.filter(|(x, y)| x.to_bits() != y.to_bits())
.count()
+ a.len().abs_diff(b.len())
};
let mut ref_streams: Vec<Vec<u32>> = Vec::with_capacity(b);
let mut ref_argmax: Vec<u32> = Vec::with_capacity(b);
let mut ref_logits: Vec<Vec<f32>> = Vec::with_capacity(b);
let mut ref_hseeds: Vec<Vec<f32>> = Vec::with_capacity(b);
let mut ref_hiddens: Vec<Vec<f32>> = Vec::with_capacity(b);
let mut ref_decode_logits: Vec<Vec<Vec<f32>>> = Vec::with_capacity(b);
let mut ref_decode_inputs: Vec<Vec<u32>> = Vec::with_capacity(b);
for p in &prompts {
let mut c = memra_engine::pp::new_cache(&e, &model.cfg, ctx)?;
let (logits, h_seed, hidden) = model.prime_cache(&e, p, &mut c, 0)?;
let mut t = argmax(&logits) as u32;
ref_argmax.push(t);
let mut stream = Vec::with_capacity(steps);
let mut dec = Vec::with_capacity(steps);
let mut inputs = Vec::with_capacity(steps);
for _ in 0..steps {
inputs.push(t);
let (l, _) = model.decode_step_h(&e, t, &mut c)?;
t = argmax(&l) as u32;
stream.push(t);
dec.push(l);
}
ref_logits.push(logits);
ref_hseeds.push(e.dtoh(&h_seed)?);
ref_hiddens.push(e.dtoh(&hidden)?);
ref_decode_logits.push(dec);
ref_decode_inputs.push(inputs);
ref_streams.push(stream);
}
let mut caches: Vec<Cache> = (0..b)
.map(|_| memra_engine::pp::new_cache(&e, &model.cfg, ctx))
.collect::<Result<_, _>>()?;
let batch0 = memra_engine::pp::step35_prime_batches();
let split0 = memra_engine::pp::step35_prime_batch_splits();
{
let prompt_refs: Vec<&[u32]> = prompts.iter().map(|p| p.as_slice()).collect();
let mut cache_refs: Vec<&mut Cache> = caches.iter_mut().collect();
let outs = model.prime_cache_batch(&e, &prompt_refs, &mut cache_refs)?;
for (s, (logits, h_seed, hidden)) in outs.iter().enumerate() {
let a = argmax(logits) as u32;
let ok = a == ref_argmax[s];
println!(
"seq {s} (T={}): prefill argmax batched={a} individual={} {}",
prompts[s].len(),
ref_argmax[s],
if ok {
"MATCH"
} else {
fails += 1;
"MISMATCH"
}
);
if exact {
let hs = e.dtoh(h_seed)?;
let hid = e.dtoh(hidden)?;
let dl = bit_diff(&ref_logits[s], logits);
let dhs = bit_diff(&ref_hseeds[s], &hs);
let dh = bit_diff(&ref_hiddens[s], &hid);
println!(
"seq {s}: exact logits diff {dl}/{} h_seed diff {dhs}/{} hidden diff {dh}/{}",
logits.len(),
hs.len(),
hid.len()
);
if dl + dhs + dh != 0 {
fails += 1;
}
}
}
}
for (s, c) in caches.iter_mut().enumerate() {
let mut stream = Vec::with_capacity(steps);
let mut dec_diff = 0usize;
for step in 0..steps {
let input = if exact {
ref_decode_inputs[s][step]
} else if step == 0 {
ref_argmax[s]
} else {
*stream.last().unwrap()
};
let (l, _) = model.decode_step_h(&e, input, c)?;
let t = argmax(&l) as u32;
stream.push(t);
if exact {
dec_diff += bit_diff(&ref_decode_logits[s][step], &l);
}
}
let ok = stream == ref_streams[s];
println!(
"seq {s}: decode-{steps} stream {}",
if ok {
"MATCH"
} else {
fails += 1;
"DIVERGED"
}
);
if exact {
println!("seq {s}: teacher-forced decode logit diff {dec_diff}");
if dec_diff != 0 {
fails += 1;
}
}
}
if exact && model.cfg.step35.is_some() {
let nb = memra_engine::pp::step35_prime_batches() - batch0;
let ns = memra_engine::pp::step35_prime_batch_splits() - split0;
let live = nb >= 1 && (!require_pp_split || ns >= 1);
println!(
"step35 batched-prime liveness: batches={nb} pp_splits={ns} require_pp_split={require_pp_split} {}",
if live { "LIVE" } else { "NOT-LIVE" }
);
if !live {
fails += 1;
}
}
if rest.iter().any(|a| a == "--carried") {
let prefixes: Vec<Vec<u32>> = (0..b)
.map(|i| {
(0..40 + i as u32 * 13)
.map(|j| 61 + i as u32 * 89 + j * 29)
.collect()
})
.collect();
let suffixes: Vec<Vec<u32>> = (0..b)
.map(|i| {
(0..18 + i as u32 * 7)
.map(|j| 77 + i as u32 * 53 + j * 37)
.collect()
})
.collect();
let mut ref_streams: Vec<Vec<u32>> = Vec::with_capacity(b);
let mut ref_argmax: Vec<u32> = Vec::with_capacity(b);
for s in 0..b {
let mut c = memra_engine::pp::new_cache(&e, &model.cfg, ctx)?;
let _ = model.prime_cache(&e, &prefixes[s], &mut c, 0)?;
let (logits, _, _) = model.prime_cache(&e, &suffixes[s], &mut c, 0)?;
let mut t = argmax(&logits) as u32;
ref_argmax.push(t);
let mut stream = Vec::with_capacity(steps);
for _ in 0..steps {
let (l, _) = model.decode_step_h(&e, t, &mut c)?;
t = argmax(&l) as u32;
stream.push(t);
}
ref_streams.push(stream);
}
let mut caches: Vec<Cache> = (0..b)
.map(|_| memra_engine::pp::new_cache(&e, &model.cfg, ctx))
.collect::<Result<_, _>>()?;
for s in 0..b {
let _ = model.prime_cache(&e, &prefixes[s], &mut caches[s], 0)?;
}
{
let suffix_refs: Vec<&[u32]> = suffixes.iter().map(|p| p.as_slice()).collect();
let mut cache_refs: Vec<&mut Cache> = caches.iter_mut().collect();
let outs = model.prime_cache_batch(&e, &suffix_refs, &mut cache_refs)?;
for (s, (logits, _, _)) in outs.iter().enumerate() {
let a = argmax(logits) as u32;
let ok = a == ref_argmax[s];
println!(
"carried seq {s} (P={},S={}): suffix argmax batched={a} single={} {}",
prefixes[s].len(),
suffixes[s].len(),
ref_argmax[s],
if ok {
"MATCH"
} else {
fails += 1;
"MISMATCH"
}
);
}
}
for (s, c) in caches.iter_mut().enumerate() {
let mut t = ref_argmax[s];
let mut stream = Vec::with_capacity(steps);
for _ in 0..steps {
let (l, _) = model.decode_step_h(&e, t, c)?;
t = argmax(&l) as u32;
stream.push(t);
}
let ok = stream == ref_streams[s];
println!(
"carried seq {s}: decode-{steps} stream {}",
if ok {
"MATCH"
} else {
fails += 1;
"DIVERGED"
}
);
}
}
if let Some(bt) = rest
.iter()
.position(|a| a == "--bench")
.and_then(|i| rest.get(i + 1))
.and_then(|v| v.parse::<usize>().ok())
{
let bp: Vec<Vec<u32>> = (0..b)
.map(|i| {
(0..bt as u32)
.map(|j| 55 + i as u32 * 97 + j * 31)
.collect()
})
.collect();
let mut seq_times = Vec::new();
let mut bat_times = Vec::new();
let run_seq = || -> Result<f64, Box<dyn std::error::Error>> {
let t0 = std::time::Instant::now();
for p in &bp {
let mut c = memra_engine::pp::new_cache(&e, &model.cfg, bt + 64)?;
let _ = model.prime_cache(&e, p, &mut c, 0)?;
}
e.stream().synchronize()?;
Ok(t0.elapsed().as_secs_f64())
};
let run_batch = || -> Result<f64, Box<dyn std::error::Error>> {
let mut cs: Vec<Cache> = (0..b)
.map(|_| memra_engine::pp::new_cache(&e, &model.cfg, bt + 64))
.collect::<Result<_, _>>()?;
let pr: Vec<&[u32]> = bp.iter().map(|p| p.as_slice()).collect();
let mut cr: Vec<&mut Cache> = cs.iter_mut().collect();
let t0 = std::time::Instant::now();
let _ = model.prime_cache_batch(&e, &pr, &mut cr)?;
e.stream().synchronize()?;
Ok(t0.elapsed().as_secs_f64())
};
for rep in 0..5 {
let (order, st, bt) = if rep % 2 == 0 {
("serial,batch", run_seq()?, run_batch()?)
} else {
let bt = run_batch()?;
("batch,serial", run_seq()?, bt)
};
println!(
"bench-run B={b} T={} rep={} order={order} serial_ms={:.3} batch_ms={:.3}",
bp[0].len(),
rep + 1,
st * 1e3,
bt * 1e3,
);
seq_times.push(st);
bat_times.push(bt);
}
seq_times.sort_by(|a, c| a.partial_cmp(c).unwrap());
bat_times.sort_by(|a, c| a.partial_cmp(c).unwrap());
let (sm, bm) = (seq_times[2], bat_times[2]);
let n = (b * bt) as f64;
println!(
"bench B={b} T={bt} N=5 alternating: serial_wall_ms={:.3} \
batch_wall_ms={:.3} sequential={:.1} tok/s batched={:.1} tok/s ({:+.1}%)",
sm * 1e3,
bm * 1e3,
n / sm,
n / bm,
100.0 * (sm / bm - 1.0),
);
}
if fails == 0 {
println!("ALL GREEN: prime-batch gate (batch={b}, uneven lengths)");
Ok(())
} else {
Err(format!("prime-batch-gate: {fails} FAIL(s)").into())
}
}