use memra_engine::Engine;
use std::time::Instant;
fn synth_e4m3(out_f: usize, in_f: usize) -> Vec<u8> {
let mut w = vec![0u8; out_f * in_f];
let mut s: u32 = 0x1234_5678;
for b in w.iter_mut() {
s = s.wrapping_mul(1664525).wrapping_add(1013904223);
let mag = ((s >> 16) & 0x7F) as u8;
let mag = if mag == 0x7F { 0x30 } else { mag };
*b = mag | ((((s >> 8) & 1) as u8) << 7);
}
w
}
fn synth_q8_0(out_f: usize, in_f: usize) -> Vec<u8> {
let nblk = in_f / 32;
let mut w = vec![0u8; out_f * nblk * 34];
let mut s: u32 = 0x9E37_79B9;
for blk in w.chunks_exact_mut(34) {
blk[0] = 0x00;
blk[1] = 0x14;
for q in blk[2..].iter_mut() {
s = s.wrapping_mul(1664525).wrapping_add(1013904223);
*q = (s >> 24) as u8;
}
}
w
}
fn gpu_temp() -> String {
std::process::Command::new("nvidia-smi")
.args(["--query-gpu=temperature.gpu,clocks.sm", "--format=csv,noheader"])
.output()
.ok()
.and_then(|o| String::from_utf8(o.stdout).ok())
.map(|s| s.trim().replace('\n', " | "))
.unwrap_or_else(|| "n/a".into())
}
fn median(v: &mut [f64]) -> f64 {
v.sort_by(f64::total_cmp);
v[v.len() / 2]
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
let iters: usize = std::env::args().nth(1).and_then(|s| s.parse().ok()).unwrap_or(200);
let set = std::env::args().nth(2).unwrap_or_else(|| "27b".to_string());
let e = Engine::new(0)?;
unsafe {
std::env::set_var("MEMRA_MMVQ", "1");
}
println!("GPU: {} iters={iters} shapes={set} temp_in: {}", e.ctx().name()?, gpu_temp());
println!("m=1 GEMV: native e4m3 (1.0 B/weight, qmatvec_e4m3_mmvq) vs Q8_0 MMVQ floor (1.0625 B/weight)");
println!("DRAM-cold (rotated copies); interleaved e4m3,q8_0 per iter; median; ratio = t_q8_0/t_e4m3");
println!(
"{:<28} {:>4} {:>10} {:>10} {:>9} {:>9} {:>9} {:>9}",
"shape in->out", "cp", "e4m3_us", "q8_0_us", "ratio", "delta_pp", "e4m3_GB/s", "q8_0_GB/s"
);
let shapes_27b: [(usize, usize, &str); 6] = [
(5120, 12288, "q_proj"),
(5120, 1024, "k/v_proj"),
(6144, 5120, "o_proj"),
(5120, 17408, "gate/up_proj"),
(17408, 5120, "down_proj"),
(5120, 5120, "square-ref"),
];
let shapes_1p7b: [(usize, usize, &str); 5] = [
(2048, 2048, "q_proj"),
(2048, 1024, "k/v_proj"),
(2048, 2048, "o_proj"),
(2048, 6144, "gate/up_proj"),
(6144, 2048, "down_proj"),
];
let shapes: Vec<(usize, usize, &str)> = if set == "1p7b" {
shapes_1p7b.to_vec()
} else {
shapes_27b.to_vec()
};
let mut sum_ln = 0.0f64;
let mut n = 0usize;
for (in_f, out_f, label) in shapes {
let rb_e4m3 = in_f;
let rb_q8_0 = (in_f / 32) * 34;
let wb_e4m3 = out_f * rb_e4m3;
let wb_q8_0 = out_f * rb_q8_0;
let copies = (768_000_000usize / wb_q8_0).clamp(1, 64);
let h_e4m3 = synth_e4m3(out_f, in_f);
let h_q8_0 = synth_q8_0(out_f, in_f);
let d_e4m3: Vec<_> = (0..copies)
.map(|_| e.htod_bytes(&h_e4m3))
.collect::<Result<_, _>>()?;
let d_q8_0: Vec<_> = (0..copies)
.map(|_| e.htod_bytes(&h_q8_0))
.collect::<Result<_, _>>()?;
drop(h_e4m3);
drop(h_q8_0);
let x: Vec<f32> = (0..in_f).map(|i| ((i % 17) as f32 - 8.0) * 0.1).collect();
let xd = e.htod(&x)?;
for c in 0..copies.min(4) {
let _ = e.qmatvec_mmvq_raw(&d_e4m3[c], &xd, 1, in_f, out_f, memra_engine::QT_F8_E4M3, rb_e4m3, false)?;
let _ = e.qmatvec_mmvq_raw(&d_q8_0[c], &xd, 1, in_f, out_f, memra_engine::QT_Q8_0, rb_q8_0, false)?;
}
e.stream().synchronize()?;
let mut t_f8: Vec<f64> = Vec::with_capacity(iters);
let mut t_q8: Vec<f64> = Vec::with_capacity(iters);
for i in 0..iters {
let c = i % copies;
let t0 = Instant::now();
let _ = e.qmatvec_mmvq_raw(&d_e4m3[c], &xd, 1, in_f, out_f, memra_engine::QT_F8_E4M3, rb_e4m3, false)?;
e.stream().synchronize()?;
t_f8.push(t0.elapsed().as_secs_f64());
let t1 = Instant::now();
let _ = e.qmatvec_mmvq_raw(&d_q8_0[c], &xd, 1, in_f, out_f, memra_engine::QT_Q8_0, rb_q8_0, false)?;
e.stream().synchronize()?;
t_q8.push(t1.elapsed().as_secs_f64());
}
let a = median(&mut t_f8);
let b = median(&mut t_q8);
let ratio = b / a;
println!(
"{:<28} {:>4} {:>10.2} {:>10.2} {:>8.4}x {:>+9.2} {:>9.1} {:>9.1}",
format!("{label} {in_f}->{out_f}"),
copies,
a * 1e6,
b * 1e6,
ratio,
100.0 * (ratio - 1.0),
wb_e4m3 as f64 / a / 1e9,
wb_q8_0 as f64 / b / 1e9
);
sum_ln += ratio.ln();
n += 1;
}
let geo = (sum_ln / n as f64).exp();
println!(
"GEOMEAN ratio (q8_0/e4m3) over {n} shapes: {geo:.4}x => delta_pp {:+.2}",
100.0 * (geo - 1.0)
);
println!("byte-stream ceiling: 34/32 = 1.0625x => +6.25pp if perfectly bandwidth-bound");
println!("temp_out: {}", gpu_temp());
Ok(())
}