Skip to content

hip: a 64-row routed prompt tile for many-expert plans on gfx1151 - #1658

Draft
gilby wants to merge 2 commits into
ashhart:mainfrom
gilby:strix/hip-routed-item64
Draft

gilby wants to merge 2 commits into
ashhart:mainfrom
gilby:strix/hip-routed-item64

Conversation

@gilby

@gilby gilby commented Oct 30, 2026

Copy link
Copy Markdown
Contributor

What this changes

A routed prompt matrix-core tile of 64 rows an item (affine_wmma_item<BITS, 4>, all 8 waves across the 128 columns, the 128-row tile's staging and fold unchanged, so rows are byte-identical), selected through a new gfx1151.zon (gfx1100's rows plus mlx.routed.prefill.item64 at cost 9 for routed plans of up to 12 pairs an item, using #1607's pairs-per-item bound). Fixes #1601.

Receipt

  • Environment: TensorFold main f350cd3e (with HIP: bound the routed prompt's K-parallel tile by pairs per item, and give it gfx1201's short prompts #1607) plus this change, tensorfold-native 1.1.8 built with zig build native-hip -Dhip-arch=gfx1151, Ubuntu archive HIP 7.1 (clang 21), AMD Ryzen AI MAX+ 395 with Radeon 8060S gfx1151 (128 GB unified memory). Checkpoints TensorFold/Qwen3.8-Flash-Next-MLX-4bit-MTP revision 2b170fa6 and an oMLX oQ4e quantization of Qwen3.8-Flash-Next, MTP 3 drafts, --parallel 1 --context 131072 --prompt-cache-gib 0 --policy prefill_step=4096, main and candidate on the same machine.
  • Exactness: token_sha256 of tools/bench_openai.py, tools/bench_concurrent.py and both tools/bench_cache.py prompts identical to main on both checkpoints; bench_concurrent --alone --serial 0 failed, 0 unmeasured, every cell equal.
  • Kernel timing, zig build hip-affine-tune on gfx1151 at Flash Next's served routed plans (512 experts plus the shared one, 11 slots), us, 128-row tile -> 64-row tile: gate/up (n 1280, k 2560) 256 tokens 10,987 -> 9,741, 512 tokens 11,576 -> 9,932, 1,024 tokens 21,215 -> 10,629, 2,048 tokens 25,580 -> 12,303; down (n 2560, k 640) 5,790 -> 3,425, 6,343 -> 3,737, 11,465 -> 4,702, 14,281 -> 6,325. Plans of 1 to 64 tokens also favour it by about 10%. Those plans hold 3.3 to 6.8 pairs an item; a 4,096-token chunk holds about 9, so the bound is 12 (not timed at 4,096 directly; the served runs below use that step).
  • Decode speed, tools/bench_openai.py --tokens 256 --reps 3 --temperatures 0: unchanged (4-bit 50.0 / 43.9 -> 50.2 / 44.1, oQ4e 42.1 / 36.2 -> 42.0 / 36.2 tok/s code / chat; decode takes the stream tiles).
  • Prompt speed, tools/bench_cache.py --require-cold --tokens 16 on public 7,377- and 29,364-token arrays, ttft_s of the cold request after the warm-up: 4-bit 20.72 -> 19.90 s and 91.79 -> 88.48 s (-4.0% / -3.6%); oQ4e 22.42 -> 21.40 s and 95.37 -> 92.40 s (-4.5% / -3.1%). One run per cell. The routed products are a larger share of prompt time once the sparse attention is faster (HIP Flash Next: WMMA sparse attention for prompt chunks on gfx11 #1597), so the gain should grow with it.
  • Tests run: zig build hip-kernel-contract on gfx1151: SUMMARY gfx1151 51 kernels: 51 pass gates 1-4, 0 fail, 0 known findings; 5 controls: 5 red as planted, 0 missed; 126 limit problems (main: 50 kernels, the same 126 live-versus-recorded register differences for existing kernels under this machine's compiler); the new tile's cases (groups 32 and 64): non-finite, repeat, shapes and sums pass, byte-equal to the 128-row tile it replaces; live limits 176 VGPRs and 34,304 LDS bytes as recorded. zig build hip-kernel-test 106/106 (the routed prompt byte-equality test now compares the new tile against the matrix, GEMM and K-parallel tiles on every routed prompt cut). zig build test -Dhip=true --summary all 468/468 steps, 0 failed. python3 tools/zig/lean_check.py 426 on main and change. git diff --check clean.
  • Not run: hip-kernel-contract-limits stops on this machine's compiler at tiles/dot2_tiles.hip for gfx1100 (branch size exceeds simm16, same on main, see HIP: dot2_tiles.hip fails to compile for gfx1100 with Ubuntu clang 21.1.8 ("branch size exceeds simm16") #1521); the records for gfx1030, gfx1100 and gfx1201 come from compiling the tile for each chip and reading its code object (gfx1030 and gfx1201 get a trapping stub, as they have no gfx11 matrix cores, and never select it). gfx1100 keeps its table unchanged (not measured there). Metal and CUDA.

Checklist

  • Every token still goes through the lane rounds. No serial path, and nothing that needs drafts off.
  • Drafted output equals "draft": false, a resumed prompt equals a fresh one, and concurrent equals solo.
  • No precision traded for speed. If the bits change, the description says which sums change and why.
  • A new or changed GPU kernel is registered with the kernel contract (docs/kernel-contract.md); each backend's SUMMARY line is in the receipt.
  • Prompt processing is no slower than the last release.
  • The description says which platforms I ran, Metal M1 to M5 and CUDA, and which I could not.
  • New tests fail before the change, pass after it, and skip cleanly without their dependency.
  • Comments and docstrings are one line. No measurements or history in the source.
  • No personal data, machine names, internal hosts or local paths. No AI attribution lines.

A routed prompt plan with hundreds of experts gives each item a few rows
to a few dozen, but the routed matrix-core tile is 128 rows, so most of
its waves skip their WMMA while still staging weights. A 64-row tile
over all waves keeps the 128-row tile's staging and fold, so its rows
are byte-identical. gfx1151 gets its own table: gfx1100's rows plus the
new tile for routed plans of up to 12 pairs an item, using the
pairs-per-item bound from ashhart#1607.

Fixes ashhart#1601
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

HIP: a 16/32-row routed-expert prompt tile for many-expert MoE (Flash Next)

1 participant