Repository navigation
Conversation
… the scalar kernel's bits
tf_fn_sparse_lanes{6,12}_{f16,bf16} do the scalar kernel's fp32
operations in its order: a row's 32-key tiles, each score a sum over
the dims in order, the per-tile xor-shuffle max and sum, the running
max and sum folded tile by tile, and P.V key by key. Only the operands'
sources change: a lane holds a key and several heads, q is read four
dims at a time, K and V are staged in the cache type with 16-byte loads
and V is read from LDS, the cache kind is a template parameter, and one
tile in 31 KB of LDS lets two blocks share a CU.
The launcher takes it for 16-bit caches with head dims on 8 and keeps
tf_fn_sparse_parts for fp32 caches. A device test compares both
kernels' parts, maxima and merged rows word for word; the contract gets
records for both cache kinds and an fp64 runner.
d719be1 to
14f04fe
Compare
|
Rebased onto current main (cfcbecb). The conflicts were the CHANGELOG, the record list and the kernel and contract registries, all resolved by keeping both sides. The merge took Receipts on the rebased head (14f04fe), one Strix Halo iGPU (gfx1151), ROCm 7.1 hipcc, zig 0.17.0:
Not run: gfx1030, gfx1100 and gfx1201 hardware, and any other family. Still waiting on a review. |
|
Thanks @gilby. A lane kernel that does the scalar kernel's fp32 operations in the same order, for 16-bit caches with head dims that are multiples of 8, is the right shape, and so is moving the two records to their own file to keep
We have no AMD GPU, so your receipts are what we go on. Then rebase, take it out of draft, and we'll land it. |
What this changes
Flash Next's sparse attention on HIP gets a second kernel,
tf_fn_sparse_lanes{6,12}_{f16,bf16}(zig/kernels/hip/flashnext/sparse_lanes.hpp), that does the same fp32 operations in the same order astf_fn_sparse_parts{6,12}and so gives the same bits: the launcher takes it for 16-bit caches whose head dim is a multiple of 8, and keeps the scalar kernel for fp32 caches. Prompt rows, decode rows and MTP verify rows all go through it, as they did through the scalar kernel.The scalar kernel spends most of its time on LDS reads (two per multiply-add) and a cache-kind branch per element. The new kernel keeps each row's 32-key tiles, each (head, key) score as a sequential dim-by-dim sum, the per-tile xor-shuffle max and sum trees, the running max/sum update tile by tile and P·V key by key, and changes only where the operands come from:
Refs #1597.
Receipt
9e8c368bplus this change,zig build native-hip -Dhip-arch=gfx1151, Ubuntu archive HIP 7.1 (clang 21), AMD Ryzen AI MAX+ 395 with Radeon 8060Sgfx1151(128 GB unified memory). CheckpointTensorFold/Qwen3.8-Flash-Next-MLX-4bit-MTPrevision2b170fa6, MTP 3 drafts,--parallel 1 --context 131072 --prompt-cache-gib 0, base and candidate alternating on fresh servers on the same machine.hip/kernel_tests/flashnext_sparse.zig) runs both kernels on the same rows and compares the parts, the maxima and sums and the merged rows word for word: bf16 and fp16 caches, 12, 6 and 3 query heads a kv head, head dims 256 and 128, 1, 3, 5 and 16 parts, key counts 0, 1, 7, 31, 32, 33, 63, 64, 65, 97, 300, 650, 2,000 and 2,048, listed and dense rows. It needs the new launcher entry, so it fails to build on main.zig build hip-kernel-test110 passed, 1 skipped, 0 failed (run directly from the test binary); with two of the kernel's additions swapped, the new test fails.tf_fn_sparse_lanes12_{f16,bf16}and a runner (zig/tests/hip/contract_sparse_lanes.zig) against an fp64 reference:SUMMARY gfx1151 53 kernels: 53 pass gates 1-4, 0 fail, 2 known findings; 5 controls: 5 red as planted, 0 missed; 130 limit problems(main: 52 kernels, the same 2 known findings and 130 limit problems). The limit problems are the same register differences in existing kernels as on main; the new records pass their live limits. All four chips' records come fromzig build hip-kernel-contract-limits -Dhip-relax-branches=true(every new record current on gfx1030, gfx1100, gfx1151 and gfx1201); the live check passes on gfx1151.token_sha256oftools/bench_openai.py,tools/bench_concurrent.pyand bothtools/bench_cache.py --require-coldprompts identical to main;bench_concurrent --alone --serialevery cell equal, 0 failed.tools/bench_cache.py --require-cold --tokens 16on public 7,377- and 29,364-token arrays, cold requests after the first (fresh servers in the order main, this, ..., main): 7,377 tokens 19.51 / 19.52 s -> 12.24 / 12.28 s (-37%), 29,364 tokens 87.22 / 86.96 s -> 53.38 / 53.35 s (-39%). The first request after start: 45.4 / 45.0 -> 38.4 s and 97.7 / 97.2 -> 62.9 s.tools/bench_openai.py --tokens 256 --reps 3 --temperatures 0, median tok/s code / chat: main 49.87 / 43.85 and 50.06 / 44.02, this change 50.57 / 44.50 (unchanged); MTP acceptance unchanged (2.753 / 2.37 tokens a round), as the bits are.tf_fn_sparse_parts129.82 s →tf_fn_sparse_lanes12_bf161.96 s, at the same 1,614 launches; the whole GPU window 21.7 -> 13.7 s (profiled on maind377fcc6before the rebase).zig build test -Dhip=true --summary all485/485 steps, 0 failed;python3 tools/zig/lean_check.py424 on main and change;zig fmt --checkandgit diff --checkclean.Checklist
"draft": false, a resumed prompt equals a fresh one, and concurrent equals solo.SUMMARYline is in the receipt.