use memra_engine::Engine;
const BLOCK_BYTES: usize = 4 * 4 + 128;
fn synth_act_q8_1(in_f: usize, m: usize, total_bytes: usize, mid: bool) -> Vec<u8> {
let mut buf = vec![0u8; total_bytes];
let ne10_padded = in_f.next_multiple_of(512);
let kblocks = ne10_padded / 128;
let mut s: u32 = 0x9E37_79B9;
for kb in 0..kblocks {
for t in 0..m {
let ib = kb * m + t;
let off = ib * BLOCK_BYTES;
if off + BLOCK_BYTES > buf.len() {
continue;
}
for sl in 0..4 {
let d: f32 = 0.0125 + (((kb + t + sl) % 5) as f32) * 0.0037;
buf[off + sl * 4..off + sl * 4 + 4].copy_from_slice(&d.to_le_bytes());
}
for q in 0..128 {
s = s.wrapping_mul(1664525).wrapping_add(1013904223);
buf[off + 16 + q] = if mid {
(0x30 + ((s >> 17) % 0x20) as u8) | ((((s >> 9) & 1) as u8) << 7)
} else {
let v = ((s >> 17) % 253) as i32 - 126; (v as i8) as u8
};
}
}
}
buf
}
fn synth_w_q8_0(in_f: usize, out_f: usize, mid: bool) -> Vec<u8> {
let nblk = in_f / 32;
let mut buf = vec![0u8; out_f * nblk * 34];
let mut s: u32 = 0x1234_5678;
for r in 0..out_f {
for b in 0..nblk {
let off = (r * nblk + b) * 34;
let d: f32 = 0.01 + (((r + b) % 7) as f32) * 0.003;
let h = f32_to_f16_bits(d);
buf[off..off + 2].copy_from_slice(&h.to_le_bytes());
for q in 0..32 {
s = s.wrapping_mul(1664525).wrapping_add(1013904223);
buf[off + 2 + q] = if mid {
(0x30 + ((s >> 17) % 0x20) as u8) | ((((s >> 9) & 1) as u8) << 7)
} else {
let v = ((s >> 17) % 253) as i32 - 126; (v as i8) as u8
};
}
}
}
buf
}
fn f32_to_f16_bits(x: f32) -> u16 {
let b = x.to_bits();
let sign = ((b >> 16) & 0x8000) as u16;
let exp = ((b >> 23) & 0xFF) as i32 - 127 + 15;
assert!(
(1..=30).contains(&exp),
"f32_to_f16_bits: {x} out of normal fp16 range"
);
let mant = b & 0x007F_FFFF;
let mut h = sign | ((exp as u16) << 10) | ((mant >> 13) as u16);
let dropped = mant & 0x1FFF;
if dropped > 0x1000 || (dropped == 0x1000 && (h & 1) == 1) {
h += 1;
}
h
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
let m: usize = std::env::args()
.nth(1)
.and_then(|s| s.parse().ok())
.unwrap_or(512);
let reps: usize = std::env::args()
.nth(2)
.and_then(|s| s.parse().ok())
.unwrap_or(9);
let set = std::env::args().nth(3).unwrap_or_else(|| "27b".to_string());
let dist = std::env::var("ACCPROBE_DIST").unwrap_or_else(|_| "wide".into());
let mid = dist == "mid";
let e = Engine::new(0)?;
println!(
"GPU: {} m={m} reps={reps} shapes={set} dist={dist}",
e.ctx().name()?
);
println!(
"ACCUMULATOR INSTRUMENT: one kernel (cu/mmq_q8_0_f32acc.cu), one variable (s32 vs f32 MMA)."
);
println!(
"interleaved f32,s32 per rep; median of reps; ratio = t_f32/t_s32; delta_pp = 100*(ratio-1)"
);
println!(
"{:<28} {:>11} {:>11} {:>9} {:>10} {:>11} {:>11}",
"shape in->out", "f32acc_ms", "s32acc_ms", "ratio", "delta_pp", "f32_TFLOP", "s32_TFLOP"
);
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_ratio = 0.0f64;
let mut n_cells = 0usize;
for (in_f, out_f, label) in shapes {
let w = synth_w_q8_0(in_f, out_f, mid);
let w_d = e.htod_bytes(&w)?;
let act_bytes = e.accprobe_act_bytes(in_f, m);
let act = synth_act_q8_1(in_f, m, act_bytes, mid);
let act_d = e.htod_bytes(&act)?;
let _ = e.accprobe_gemm(&w_d, &act_d, m, in_f, out_f, true)?;
let _ = e.accprobe_gemm(&w_d, &act_d, m, in_f, out_f, false)?;
e.stream().synchronize()?;
let mut t_f32: Vec<f64> = Vec::with_capacity(reps);
let mut t_s32: Vec<f64> = Vec::with_capacity(reps);
for _ in 0..reps {
let t0 = std::time::Instant::now();
let _ = e.accprobe_gemm(&w_d, &act_d, m, in_f, out_f, true)?;
e.stream().synchronize()?;
t_f32.push(t0.elapsed().as_secs_f64());
let t1 = std::time::Instant::now();
let _ = e.accprobe_gemm(&w_d, &act_d, m, in_f, out_f, false)?;
e.stream().synchronize()?;
t_s32.push(t1.elapsed().as_secs_f64());
}
t_f32.sort_by(f64::total_cmp);
t_s32.sort_by(f64::total_cmp);
let (a, b) = (t_f32[reps / 2], t_s32[reps / 2]);
let ratio = a / b;
let flop = 2.0 * m as f64 * in_f as f64 * out_f as f64;
println!(
"{:<28} {:>11.4} {:>11.4} {:>8.3}x {:>+10.1} {:>11.1} {:>11.1}",
format!("{label} {in_f}->{out_f}"),
a * 1e3,
b * 1e3,
ratio,
100.0 * (ratio - 1.0),
flop / a / 1e12,
flop / b / 1e12
);
sum_ln_ratio += ratio.ln();
n_cells += 1;
}
let geo = (sum_ln_ratio / n_cells as f64).exp();
println!(
"GEOMEAN ratio (f32/s32) over {n_cells} shapes: {geo:.4}x => delta_pp {:+.1}",
100.0 * (geo - 1.0)
);
println!(
"READ: delta_pp is what s32 accumulation is worth at fixed geometry — an UPPER BOUND on a v3."
);
Ok(())
}