Files
Hanzo Dev 09bdb85193 hanzo-ml 0.11.88, hanzo-rocm-kernels 0.11.36: qmmq Q4_K/Q5_K min-bias precompute + Vulkan int8 KV-quant
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.
2026-07-21 16:54:57 -07:00
..

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:

  1. Source Code: HIP kernels are shipped as source code (.hip files)
  2. On-Demand Compilation: First time a kernel is needed, it's compiled using hipcc
  3. Caching: Compiled binaries are cached for reuse
  4. 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 rocminfo or falls back to environment variable HANZO_ROCM_ARCH
  • Version Detection: Detects ROCm version using hipcc --version or environment variable HANZO_ROCM_VERSION
  • Compilation: Invokes hipcc with 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 file
  • Module::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