Repository navigation
[ROCm][MiniMax-M3] MXFP4 K/V and index cache (mxfp4) on gfx950 - #64198
ganyi1996ppo wants to merge 6 commits into
Conversation
|
Documentation preview: https://vllm--64198.org.readthedocs.build/en/64198/ |
|
This pull request has merge conflicts that must be resolved before it can be |
…de.MXFP4 = 16) Move KVQuantMode.MXFP4 from 15 to 16, since 15 is MXFP4_DS_MLA in vllm-project#64120, and make the cache.py, torch_utils.py and kv_cache_interface.py hunks identical to c7f5944 in vllm-project#64198 so whichever PR lands second drops a clean duplicate. Signed-off-by: Limpus, David <dlimpus@amd.com>
…VQuantMode.MXFP4 = 16)" This reverts commit 762bcf8. Signed-off-by: Limpus, David <dlimpus@amd.com>
15 is MXFP4_DS_MLA in vllm-project#64120 and 16 is MXFP4 in vllm-project#64198. Signed-off-by: Limpus, David <dlimpus@amd.com>
15 is MXFP4_DS_MLA in vllm-project#64120 and 16 is MXFP4 in vllm-project#64198. Signed-off-by: Limpus, David <dlimpus@amd.com> (cherry picked from commit b3a9d3e)
Register `--kv-cache-dtype mxfp4` (uint8 storage, `KVQuantMode.MXFP4`) and the MXFP4 page layout the MiniMax-M3 K/V and index caches use on gfx950: per head, 128 tokens x 64 B of e2m1 data followed by 128 tokens x 4 B of e8m0 scales, 8704 B in all. The dtype is not accepted by any attention path yet. Signed-off-by: ganyi <ygan@amd.com> Co-Authored-By: Claude <noreply@anthropic.com>
Serve MiniMax-M3.1 with `--kv-cache-dtype mxfp4` and `indexer_kv_dtype="mxfp4"` on gfx950, TP4 (one KV head per rank): - the fused QK-norm / RoPE kernel writes MXFP4 K, V and index-K pages and hands back the main q as e4m3 (new `fused_qknorm_idxrqknorm_mxfp4` wrapper); - the index scores come from AITER's FlyDSL MXFP4 scorers, the forced init/local blocks are pinned with AITER's sentinels, and the top-k is the one the NVFP4 indexer uses; - the attend turns each query token's top-k into a one-block-per-page table and runs AITER's `pa_decode_mxfp4` on it, for decode and prefill. The sparse block-table builders gain a `pages_per_block` argument (1 for a page-128 cache). Needs an AITER with the MXFP4 FlyDSL ops (ROCm/aiter#7056, vllm-project#7057, vllm-project#7060, vllm-project#7061). Mono, indexer CP, other TP sizes and the draft layers are not covered. Signed-off-by: ganyi <ygan@amd.com> Co-Authored-By: Claude <noreply@anthropic.com>
…rite Follow the final ROCm/aiter#7057 API: `fused_qknorm_idxrqknorm` no longer has `q_fp8_out` / `q_scale`; an e4m3 `q_out` is written with a saturating cast at scale 1.0 and no bf16 q row is written. The bytes are those `q_fp8_out` gave. Signed-off-by: ganyi <ygan@amd.com> Co-Authored-By: Claude <noreply@anthropic.com>
…ph replay The MXFP4 index scorer's work map was allocated by every `build()` and sized from the step's `max_seq_len`. A FULL decode graph bakes in the address of the map it was captured with and the scorer's grid (the map's row count), so a replay read a map that was no longer allocated, and one built for a different block bound. On the first 32-request burst this faults with `hipErrorIllegalAddress`. Refill one persistent buffer (sized with AITER's `work_map_capacity`) every step and always build the map, and score the decode rows, with the model-length block bound the graph is captured with. Signed-off-by: ganyi <ygan@amd.com> Co-Authored-By: Claude <noreply@anthropic.com> Signed-off-by: ganyi <ygan@amd.com>
d24ae8f to
ffd9f4a
Compare
Annotate the fake aiter module as Any (its attributes are set dynamically) and narrow the Optional decode metadata before reading max_seq_len. The assertions are unchanged. Co-Authored-By: Claude Code <noreply@anthropic.com> Signed-off-by: ganyi <ygan@amd.com>
ROCm/aiter#7056 merges the standalone pa_decode_mxfp4 entry into pa_decode. Call pa_decode(out, q, k_data, v_data, ctx, bt, scale, 1, key_scale=, value_scale=, work_plan=None, max_context_length=) instead; max_partitions=1 is dropped because a plan-free call already runs one task per row. The availability probe imports pa_decode. Depends on ROCm/aiter#7056. Co-Authored-By: Claude Code <noreply@anthropic.com> Signed-off-by: ganyi <ygan@amd.com>
|
Pushed 91d7ee1 (one fast-forward commit on top of d6dbcca): the MXFP4 sparse attend now calls aiter's Why: ROCm/aiter#7056 (head f017c23591) merges the standalone Tested on gfx950 against aiter#7056 f017c23591 (same PA files), from an archive of this commit overlaid on the vllm nightly rocm100 image (0.32.0rc1, torch 2.13; python overlay, not a source build): |
Overview
Adds
--kv-cache-dtype mxfp4with--attention-config '{"indexer_kv_dtype": "mxfp4"}'for MiniMax-M3.1 on ROCm gfx950 (MI355X, TP4): K, V and the indexer's key cache are stored as MXFP4 (e2m1 values, one e8m0 scale per 32), 68 B per token and head. Reference implementation: ROCm/ATOM#2770.Draft; not runnable yet. #64111 has merged, so this is now rebased onto main (
16d667348d) as 5 commits (4 feature commits plus a test-only type fix). It still waits on three open AITER PRs (#7056, #7060, #7061), merged #7057, and an AITER pin bump (see Details). Placeholder to claim the work and get early design feedback.AI-assisted (Claude Code). No human has reviewed the diff line by line yet; this stays a draft until that is done.
Claims
mxfp4(uint8 storage,KVQuantMode.MXFP4) and a gfx950 attend for MiniMax-M3 sparse attention. Per head a 128-token page is[128 x 64 B e2m1][128 x 4 B e8m0]= 8704 B; the main K/V spec isnum_head_slots=2*Hkv, state_content_bytes=68, the index spechead_size=68.fused_qknorm_idxrqknorm) writes the MXFP4 K, V and index-K pages and writes the main q as e4m3 intoq_out.pa_decode_mxfp4on a one-block-per-page table, for decode and prefill.Validation
Rebased onto main
16d667348d(live main tip when this was pushed);git range-diffagainst the previous headd24ae8f7f2shows the 4 feature commits patch-identical; the 5th commit (d6dbccaac4) only touchestests/models/minimax_m3/test_mxfp4_kv_cache.py. Checked on a CPU-only host, no GPU used:pytest tests/models/minimax_m3/test_mxfp4_kv_cache.py tests/kernels/attention/test_minimax_m3_mxfp4.pypre-commit run(all hooks, including mypy) on the 14 changed files, atd6dbccaac4ffd9f4a06dhad 3 mypy errors intests/models/minimax_m3/test_mxfp4_kv_cache.py(dynamic attributes on the fake aiter module, an Optionaldecode.max_seq_len); fixed in the 5th commit by typing the fake module asAnyand assertingdecode is not None, assertions unchanged.CI at
d6dbccaac4: pre-commit and pre-run-check pass; docs build was still pending when this was written.Not validated after the rebase: any GPU run. The kernel tests, end-to-end serving, CUDA-graph capture, accuracy and speed were not re-run on this head. Results quoted in earlier versions of this description were measured on the pre-rebase head
d24ae8f7f2on an ROCm image and are not repeated as evidence for this head. CI has no gfx950 job that can run these ops: the kernel tests skip until the AITER pin carries them.For orientation only, ATOM's own measurements of the same kernels (ATOM#2770, MiniMax-M3.1 TP4, MXFP4 KV and index against the NVFP4 path): c32 +7.4% throughput and +26.2% P90 interactivity, c48 +8.1% and +32.3%; KV capacity 13.4M against 7.3M tokens for fp8. These are ATOM results, not vLLM results.
Details
Prerequisites. ROCm/aiter#7057 (fused write kernel: MXFP4 K/V and index-K, e4m3
q_out; this PR follows its final API, in whichq_fp8_outandq_scaleare gone) has merged; the remaining ROCm/aiter#7056 (FlyDSLpa_decode_mxfp4), #7060 (index decode scorer) and #7061 (index prefill scorers) need to merge, then an AITER pin bump indocker/Dockerfile.rocm_base, then this PR. The pin is not bumped here. AITER is imported inside functions behindVLLM_ROCM_USE_AITER=1; without the ops the layer raises a message naming those PRs.Commits (on
16d667348d):99556ddca5Add the mxfp4 KV cache dtype and its page geometry (no behavior change on its own)507ac36aefMXFP4 K/V and index cache on gfx950 (indexer, attend, write, tests, docs)06fdab1145Pass the e4m3 main q asq_outto the aiter MXFP4 writeffd9f4a06dKeep the MXFP4 decode work map stable for CUDA-graph replayd6dbccaac4Fix mypy errors intest_mxfp4_kv_cache.py(test-only)Why not a duplicate. No open or merged PR stores MiniMax-M3 K/V or index keys as MXFP4. #63547 is fp8 KV, #45838 is MXFP4 experts, #59739 (
mxfp4_mla) and #64120 (mxfp4_ds_mla) are MLA/DSv4 caches. The dtype stringmxfp4is a distinct exact-match name frommxfp4_mlaandmxfp4_ds_mla;KVQuantMode.MXFP4 = 16avoids the 15 #64120 takes.Design.
is_mxfp4dispatch inamd/model.py, a sibling indexer and sparse backend), and reuses its metadata builders, FlyDSL top-k and the existingsparse_pablock-table kernels, which gain apages_per_blockargument (1 for a page-128 cache).pa_sparse_block_topkis not used: its build only acceptspages_per_block == 8, and it expects scores already pinned by the scorer, which the MXFP4 scorers do not do. A small Triton kernel writes AITER's init/local sentinels (1e30 / 1e29) on each row's causal blocks, and the existing top-k reads only a row's causal blocks, so the scorer's unwritten entries are never seen.Size. About 850 lines of non-test code, most of it the two new files (
indexer_mxfp4.py,sparse_attention_mxfp4.py) that parallel the NVFP4 ones, plus about 700 lines of tests. Docs: one line indocs/features/quantization/quantized_kvcache.md.Pull Request Checklist
I used vLLM's
/pr-checklistskill. (Mandatory for agents, optional for humans).AI assistance was used during the creation of this PR.
Design Fit: Minimizes impact on core components, reuses existing functionality, and justifies added complexity.
Testing and Validation: Validates the change and ensures any added tests are meaningful and reliable, with CI coverage or documented CI resource constraints and validation performed outside CI.
Code Quality and Style: Keeps code and comments clear and concise, and updates relevant documentation and examples.
Pull Request Contents: Includes a brief summary and relevant links, supports claims with evidence, explains root causes and implementation trade-offs, and follows the contributing guide.
🤖 Generated with Claude Code