ROCm qmmq asymmetric min-bias precompute (hanzo-rocm-kernels) ------------------------------------------------------------ The int8-WMMA prefill GEMM (qmmq_core<WTYPE>) carries a min-bias for the sub-block-min k-quants: out -= dmin_w * m_g * d_x * sum_k(q_x). The per-block activation scale d_x was recomputed inside the per-output inner loop (one extra multiply per output element per K-block). Fold it once per activation block at quantize time: quantize_q8_1 now stores xs = d_x * sum(q_x) (f32, llama block_q8_1 `s`) instead of the raw int sum, so the epilogue reads acc -= xs * (dmin*m) -- the d_x hoisted out of the hot loop. xs changes i32 -> f32 across the quantize kernels, qmmq_core, the DEFINE_QMMQ/MoE launchers and the rocm_backend binding. Bit-exact by construction: __half2float(dh) * (float)qsum is exactly the product the old epilogue formed from the stored f16 scale, so every per-block bias term is IEEE-identical. Verified on evo (gfx1151) with Qwen3-4B-Q4_K_M greedy decode: output byte-identical pre/post (401 chars). rocprofv3 SQ_INSTS_VALU per dispatch, 648/648 dispatches: qmmq_q4k_f16 -6.6% (the asymmetric target); qmmq_q6k_f16 +0.01% (symmetric control -- the !SYMM branch elides, unchanged as required). Symmetric weight types never read xs; decode matvec (rows==1) is a separate kernel and is untouched. Vulkan int8 per-token KV-quant paged attention (opt-in) ------------------------------------------------------- Symmetric per-token int8 KV cache (KIVI / KVQuant): each cached per-head K/V vector is quantized to signed int8 with one f32 scale (amax/127), codes packed 4-per-u32 with a co-located scale word, so a token vector is self-describing and paged by slot. Two kernels: reshape_and_cache_q8 (amax reduce -> scale -> pack on write) and paged_attn_q8 (dequantize-on-read code*token_scale, same online-softmax as paged_attn). Opt-in via --pa-cache-type int8; default Auto is byte-unchanged. Verified on evo (gfx1151, RADV): the U32 int8 cache path is active with no fallback; int8 decode is argmax-identical to f16 at 128 greedy tokens and coherent with exact ordered 12-item recall at 256 (information-faithful under the lossy KV). The engine's hanzo-paged-attn::quant CPU oracle (6 tests) gates the packing. Committed SPIR-V regenerated for glslc-less hosts.
hanzo-ml-rocm-kernels
ROCm/HIP kernel support for the Hanzo deep learning framework.
Overview
This crate provides ROCm (AMD GPU) kernel support for Hanzo. Unlike CUDA which can embed PTX directly, ROCm/HIP requires ahead-of-time (AOT) compilation for specific GPU architectures.
Architecture
AOT Cache System
We use an Ahead-of-Time (AOT) compilation cache approach:
- Source Code: HIP kernels are shipped as source code (
.hipfiles) - On-Demand Compilation: First time a kernel is needed, it's compiled using
hipcc - Caching: Compiled binaries are cached for reuse
- Future Runs: Load cached binaries directly (no recompilation)
Cache Location
Compiled binaries are stored at:
~/.cache/hanzo-ml-rocm/{arch}-{rocm_version}/
For example:
~/.cache/hanzo-ml-rocm/gfx908-6.1/binary_a1b2c3d4.cso~/.cache/hanzo-ml-rocm/gfx942-6.2/binary_a1b2c3d4.cso
Where:
{arch}= GPU architecture (gfx908, gfx90a, gfx942, etc.){rocm_version}= ROCm version (6.0, 6.1, 6.2, etc.){hash}= SHA256 hash of source code (first 16 chars)
Key Components
CacheManager (src/cache.rs)
Manages the AOT compilation cache:
- GPU Detection: Automatically detects GPU architecture using
rocminfoor falls back to environment variableHANZO_ROCM_ARCH - Version Detection: Detects ROCm version using
hipcc --versionor environment variableHANZO_ROCM_VERSION - Compilation: Invokes
hipccwith appropriate flags:hipcc --offload-arch={arch} -O3 -fPIC -c -o output.o input.hip - Caching: Stores compiled
.cso(code object) files with source hash versioning
Usage:
use hanzo_rocm_kernels::CacheManager;
use rocm_rs::hip::Device;
let device = Device::new(0)?;
let cache = CacheManager::new(&device)?;
let binary = cache.get_or_compile("binary", source_code)?;
KernelManager (src/manager.rs)
Higher-level manager that:
- Wraps CacheManager
- Loads compiled binaries as
rocm_rs::hip::Module - Returns
Arc<Module>for thread-safe sharing - Maintains in-memory module cache
Usage:
use hanzo_rocm_kernels::KernelManager;
use hanzo_rocm_kernels::source::Source;
let device = Device::new(0)?;
let manager = KernelManager::new(&device)?;
let module = manager.get_or_compile_module(Source::Binary)?;
Environment Variables
HANZO_ROCM_ARCH- Override GPU architecture detection (e.g., "gfx908")HANZO_ROCM_VERSION- Override ROCm version detection (e.g., "6.1")
Requirements
- ROCm/HIP installed (provides
hipcc) - AMD GPU with supported architecture
Kernel Types
Currently supports:
- Binary operations: Add, Sub, Mul, Div, Minimum, Maximum
Building
cd hanzo-ml-rocm-kernels
cargo build
Note: First build will compile dependencies. No GPU required for building, but hipcc must be in PATH if you want to compile kernels.
Testing
cargo test
Implementation Notes
Why AOT instead of JIT?
The rocm-rs crate (v0.5) doesn't support runtime compilation. It only supports:
Module::load(path)- Load from fileModule::load_data(bytes)- Load from bytes
This makes JIT compilation (via hiprtc) unavailable, so we compile ahead-of-time on first run.
Supported GPU Architectures
Common AMD GPU architectures:
- CDNA2: gfx90a (MI200 series)
- CDNA3: gfx942 (MI300 series)
- RDNA3: gfx1100, gfx1101, gfx1102 (RX 7000 series)
The system will try to auto-detect, but you can override with HANZO_ROCM_ARCH.
License
MIT OR Apache-2.0