use hanzo_kernel::mmq::*;
use hanzo_kernel::prelude::*;
use std::panic::{catch_unwind, AssertUnwindSafe};
fn max_rel(a: &[f32], b: &[f32]) -> f32 {
let mut m = 0f32;
for (x, y) in a.iter().zip(b.iter()) {
m = m.max((x - y).abs() / x.abs().max(1e-6));
}
m
}
fn err_robust(a: &[f32], b: &[f32]) -> (f32, f32) {
let mut maxabs = 0f32;
let mut refmax = 1e-9f32;
for (x, y) in a.iter().zip(b.iter()) {
maxabs = maxabs.max((x - y).abs());
refmax = refmax.max(x.abs());
}
(maxabs, maxabs / refmax)
}
fn rand_i8(n: usize, seed: u64) -> Vec<i8> {
let mut s = seed;
(0..n)
.map(|_| {
s ^= s << 13;
s ^= s >> 7;
s ^= s << 17;
((s % 255) as i64 - 127) as i8
})
.collect()
}
fn probe<R: Runtime>(name: &str, client: &ComputeClient<R>) {
let client = &client.clone();
let plane = client.properties().hardware.plane_size_max;
let target = Target::of(client);
println!("== hanzo-kernel MMQ probe ({name}, plane={plane}, target={target:?}) ==\n");
let r = catch_unwind(AssertUnwindSafe(|| {
let a = rand_i8(256, 0x1234);
let b = rand_i8(256, 0x9E37);
let got = wmma_hello_i8_run::<R>(client, &a, &b);
let want = hello_i8_ref(&a, &b);
let bad = got.iter().zip(&want).filter(|(x, y)| x != y).count();
println!(
"[1a] WMMA hello i8 16x16x16->i32 {} ({} / 256 mismatched)",
if bad == 0 {
"EXACT ✓"
} else {
"MISMATCH ✗"
},
bad
);
bad == 0
}));
if r.is_err() {
println!("[1a] WMMA hello i8 16x16x16->i32 PANIC/JIT-FAIL ✗ (see stderr above)");
}
if matches!(target, Target::Cuda | Target::Rocm) {
let r = catch_unwind(AssertUnwindSafe(|| {
let a = rand_i8(16 * 32, 0xABCD);
let b = rand_i8(32 * 8, 0x5555);
let got = mma_hello_i8_run::<R>(client, &a, &b, plane);
let want = mma_hello_ref(&a, &b);
let bad = got.iter().zip(&want).filter(|(x, y)| x != y).count();
println!(
"[1b] manual MMA i8 m16n8k32->i32 {} ({} / 128 mismatched)",
if bad == 0 {
"EXACT ✓"
} else {
"MISMATCH ✗"
},
bad
);
bad == 0
}));
if r.is_err() {
println!("[1b] manual MMA i8 m16n8k32->i32 PANIC/JIT-FAIL ✗ (see stderr above)");
}
} else {
println!("[1b] manual MMA i8 m16n8k32->i32 N/A (no fragment layout on {target:?})");
}
for (m, n, k) in [(16usize, 16usize, 64usize), (32, 64, 256), (64, 128, 512)] {
let r = catch_unwind(AssertUnwindSafe(|| {
let (xq, xs, wq, wd) = gen_mmq(m, n, k);
let (got, _) = mmq_q8_wmma_run::<R>(client, &xq, &xs, &wq, &wd, m, n, k, 1);
let want = mmq_q8_ref(&xq, &xs, &wq, &wd, m, n, k);
let rel = max_rel(&want, &got);
let (maxabs, relmax) = err_robust(&want, &got);
println!(
"[2 ] MMQ {}x{}x{:<4} per-elt max_rel={:.2e} max_abs={:.2e} rel_to_max={:.2e} {}",
m, n, k, rel, maxabs, relmax,
if relmax < 1e-3 { "BIT-CLOSE ✓" } else { "MISMATCH ✗" }
);
}));
if r.is_err() {
println!("[2 ] MMQ {m}x{n}x{k} PANIC/JIT-FAIL ✗");
}
}
let r = catch_unwind(AssertUnwindSafe(|| {
let (m, n, k) = (512usize, 4096usize, 4096usize);
let (xq, xs, wq, wd) = gen_mmq(m, n, k);
let flop = 2.0 * m as f64 * n as f64 * k as f64;
let wbytes = (n * k) as f64; let mrows = 8usize;
let want = mmq_q8_ref(&xq, &xs, &wq, &wd, mrows, n, k);
let (g0, ms0) = mmq_q8_wmma_run::<R>(client, &xq, &xs, &wq, &wd, m, n, k, 50);
let (a0, r0) = err_robust(&want, &g0[..mrows * n]);
println!(
"\n[3 ] GEMM {m}x{n}x{k} (naive 1-warp/16x16 tile) verify rel_to_max={:.2e} max_abs={:.2e} {}",
r0, a0, if r0 < 3e-3 { "✓" } else { "✗" }
);
println!(
" {:.3} ms/dispatch {:.0} GFLOP/s {:.0} GB/s (W-stream)",
ms0,
flop / (ms0 * 1e6),
wbytes / (ms0 * 1e6)
);
let (g1, ms1) = mmq_q8_wmma_blk_run::<R>(client, &xq, &xs, &wq, &wd, m, n, k, 50);
let (a1, r1) = err_robust(&want, &g1[..mrows * n]);
println!(
"[3 ] GEMM {m}x{n}x{k} (tiled 8-warp/32x64 tile) verify rel_to_max={:.2e} max_abs={:.2e} {}",
r1, a1, if r1 < 3e-3 { "✓" } else { "✗" }
);
println!(
" {:.3} ms/dispatch {:.0} GFLOP/s {:.0} GB/s (W-stream) [{:.2}x the naive kernel]",
ms1, flop / (ms1 * 1e6), wbytes / (ms1 * 1e6), ms0 / ms1
);
}));
if r.is_err() {
println!("[3 ] GEMM 512x4096x4096 PANIC/JIT-FAIL ✗");
}
}
fn main() {
#[cfg(feature = "cuda")]
{
use cubecl::cuda::{CudaDevice, CudaRuntime};
probe::<CudaRuntime>("CUDA", &CudaRuntime::client(&CudaDevice::default()));
}
#[cfg(feature = "vulkan")]
{
use cubecl::wgpu::{WgpuDevice, WgpuRuntime};
probe::<WgpuRuntime>("VULKAN", &WgpuRuntime::client(&WgpuDevice::default()));
}
}