[ROCm][Kimi-K3] Add opt-in gfx942 MXFP4-to-int4 conversion - #51274
[ROCm][Kimi-K3] Add opt-in gfx942 MXFP4-to-int4 conversion#51274maeehart wants to merge 11 commits into
Conversation
|
I have read the DCO document and hereby sign off past commits made by me. |
410a582 to
be8095e
Compare
|
I have read the DCO document and hereby sign off on the commits in this pull request. |
|
This pull request has merge conflicts that must be resolved before it can be |
a694c54 to
0b152a8
Compare
1575b38 to
10e3a00
Compare
|
This pull request has merge conflicts that must be resolved before it can be |
10e3a00 to
2110215
Compare
|
Hi @maeehart, do you think this could use #55684 ? This would involve:
|
|
This pull request has merge conflicts that must be resolved before it can be |
|
@fxmarty-amd I looked at routing this through #55684. I would keep this PR on the current #55684 is still open and linear-only. The decode path we measured is AITER FlyDSL a16w4 with SiTUv2 through Five-shot GSM8K on this path is 0.9598 strict-match on 1,319 questions, stderr 0.0054. MoE requant through the online API can follow once #55684 covers MoE. I do not want to block this opt-in gfx942 path on that. |
|
@dllehr-amd Can you review this for merge if you agree with keeping the current Five-shot GSM8K held at 0.9598 strict-match on 1,319 questions, stderr 0.0054. I replied to @fxmarty-amd on the online-requant thread and to @simondanielsson on the inline comments. The leftover duplicate SiTU entry is removed in 6a0a733. Mergify currently reports merge conflicts with main, so a rebase is still needed before merge. |
gfx942 (MI325X, MI300X) has no native MXFP4 matmul, so the day-0 Kimi-K3 expert path falls through to code that dies in LLVM codegen. Convert the MXFP4 expert weights to int4 with groupwise bf16 scales at load time and let the existing FlyDSL SiTU stage1 kernel consume them. The gfx950 native MXFP4 path is left untouched. Two supporting changes are needed for the same reason. The AITER MoE expert backend refuses kMxfp4Static outside gfx950 and does not list the SiTU activation as supported. The AITER top-k/top-p sampler is a gfx950-only prebuilt that segfaults on gfx942, so sampling falls back to the native torch implementation there. Co-authored-by: Cursor <cursoragent@cursor.com> (cherry picked from commit 2253680) Signed-off-by: Markus Hartikainen <markus.hartikainen@amd.com> Signed-off-by: Hartikainen <mahartik@amd.com>
The gfx942 path requantized Kimi-K3's MXFP4 experts to groupwise int4 whenever the hardware matched, without the user asking. The conversion is lossy, so it is now opt-in through --quantization-config.moe.weight int4 and gfx942 keeps the native MXFP4 path otherwise. Also refuse to load when the installed AITER predates ROCm/aiter#4471. Before that fix the packed-int4 stage1 dropped the requested activation and hardcoded SiLU, so Kimi-K3 served fluent text while computing SiLU instead of the SiTUv2 its config asks for. Signed-off-by: Markus Hartikainen <markus.hartikainen@amd.com> Signed-off-by: Hartikainen <mahartik@amd.com>
The gfx942 Kimi-K3 path asks for groupwise int4 MoE weights through --quantization-config.moe.weight, but QUANT_KEY_NAMES had no name for that scheme, so the flag was rejected before the path could be selected. Register the existing kInt4Static32 key under int4_per_group_32 and match on the parsed QuantKey rather than a raw string. Signed-off-by: Markus Hartikainen <markus.hartikainen@amd.com> Signed-off-by: Hartikainen <mahartik@amd.com>
mxfp4_to_f32 splits packed nibbles with repeat_interleave and then gathers through an f32 lookup table, so the working tensor is 8x the packed weight before per_1x32_i4_quant shrinks it again. Materializing that for a whole expert tensor peaks above 20 GiB per rank and fails once the weights are resident, which is what pure tensor parallel hits since it holds all experts per rank. Convert 8 experts at a time and free each slice, bounding the transient without changing the result. Signed-off-by: Markus Hartikainen <markus.hartikainen@amd.com> Signed-off-by: Hartikainen <mahartik@amd.com>
The ROCm branch of mxfp4_round_up_hidden_size_and_intermediate_size() rounds the per-partition intermediate size up to 256. Kimi-K3 has moe_intermediate_size 3072, so a TP8 shard is 384 and gets rounded to 512. That inflates every w13 tensor from (896, 768, 1792) to (896, 1024, 1792), a 33 percent increase on all 92 MoE layers, which is about 38 GiB per rank. Pure TP8 then sits at 248.69 GiB resident with 2.12 GiB free and dies during the int4 conversion. The round-up is not needed here. AITER's resolve_flydsl_stage1_tile_n() already downgrades tile_n from 256 to 128 for a non-256-aligned inter_dim, and 128 divides 384 exactly. Mxfp4MoEMethod already skipped the round-up for the gfx950 SiTU path, so extend the same condition to the gfx942 int4 path. Measured on 8 MI325X at TP8 without expert parallel, max-model-len 16384, gpu-memory-utilization 0.97: resident drops from 248.69 GiB to 192.51 GiB, which matches the expert-parallel figure exactly, free rises from 2.12 GiB to 58.61 GiB, all 96 shards load with zero out-of-memory workers, and the KV cache is 948,305 tokens. Signed-off-by: Markus Hartikainen <markus.hartikainen@amd.com> Signed-off-by: Hartikainen <mahartik@amd.com>
Keep the draft focused by dropping the unrelated sampler fallback. Add unit coverage for the groupwise-int4 key, explicit gfx942 selection, and native Kimi-K3 intermediate shape. Make the lossy conversion visible in startup logs and use the accelerator cache API while bounding conversion memory. Signed-off-by: Markus Hartikainen <markus.hartikainen@amd.com> Signed-off-by: Hartikainen <mahartik@amd.com>
Current main already preserves Kimi-K3's native AITER intermediate shape. Drop the duplicate early return and its redundant test. Also remove a stale unreachable setup tail left by porting the original broader branch. Signed-off-by: Markus Hartikainen <markus.hartikainen@amd.com> Signed-off-by: Hartikainen <mahartik@amd.com>
Switch the capability check to AITER's replacement A16W4/int4 entry point because the original packed-int4 implementation is being removed. Co-authored-by: Cursor <cursoragent@cursor.com> Signed-off-by: Markus Hartikainen <markus.hartikainen@amd.com>
Drop the unused layer argument from the gfx942 int4 kernel factory call so the branch passes static type checking. Signed-off-by: Markus Hartikainen <markus.hartikainen@amd.com> Co-authored-by: Cursor <cursoragent@cursor.com> Signed-off-by: Hartikainen <mahartik@amd.com>
The rebase conflict resolution used backend_to_kernel_cls and os without importing them, which made mypy fail in pre-commit. Signed-off-by: Markus Hartikainen <markus.hartikainen@amd.com> Signed-off-by: Hartikainen <mahartik@amd.com> Co-authored-by: Cursor <cursoragent@cursor.com>
yes, makes sense, we are still a bit far from it, I agree.
Well, this is the reason why oracles / MOE backends and abstractions as vllm/vllm/model_executor/layers/fused_moe/modular_kernel.py Lines 628 to 634 in 8359e15 IMO ideally, this should make use of https://github.com/vllm-project/vllm/blob/main/vllm/model_executor/layers/fused_moe/oracle/int_wna16.py that is supposed to handle MOE backends for The MOE oracle refactor was meant to separate the:
and having the re-quantization step, custom backend selection & processing all under I don't think this is blocking, just nit comment! |
6a0a733 to
0fc3721
Compare
SiTU was already in _supports_activation. The second entry was a rebase leftover. Signed-off-by: Markus Hartikainen <markus.hartikainen@amd.com> Co-authored-by: Cursor <cursoragent@cursor.com> Signed-off-by: Hartikainen <mahartik@amd.com> Co-authored-by: Cursor <cursoragent@cursor.com>
|
I have read the DCO document and hereby sign off on the commits in this pull request. |
1 similar comment
|
I have read the DCO document and hereby sign off on the commits in this pull request. |
|
@fxmarty-amd Agreed. I would keep this PR as the load-time special case.
The checkpoint is still MXFP4. The int4 tensors exist only after A follow-up can add AITER packed-int4 to the WNA16 oracle, or add |
0fc3721 to
97eb956
Compare
|
Can you get this reviewed @dllehr-amd. We have discussed this PR also separately and the design concerns are now resolved. |
Summary
Add an explicit
int4_per_group_32MoE override for Kimi-K3 on gfx942. When selected, the MXFP4 expert weights are requantized once at load time and served through AITER's replacement BF16 x packed-int4 FlyDSL path.The conversion is lossy and never happens by architecture alone. The default MXFP4 path is unchanged.
Why
gfx942 does not support the scaled FP4 conversion instructions used by the native MXFP4 path. This opt-in path pays the conversion cost once during model loading and then uses the replacement packed-int4 kernels from ROCm/aiter#4646.
Changes
int4_per_group_32as a MoE weight quantization key.--quantization-config.moe.weight int4_per_group_32override on gfx942.Validation
Unit tests:
Strict AITER comparison against the torch reference passed for the production EP shape:
Re-checked on 8x MI325X with TP8, expert parallelism, Triton RMSNorm, Triton MLA, AITER fused MoE, and full plus piecewise graph capture, using the replacement packed-int4 path from ROCm/aiter#4646 (AITER v0.1.21.post2, picked up in #55968).
lm_evallocal-completions(num_fewshot=5,temperature=0.0,max_gen_toks=512) scored 0.9598 strict-match and 0.9598 flexible-extract on all 1,319 questions, stderr 0.0054. That is within noise of the 0.9560 A16W4 baseline (stderr 0.0056) on the same 1,319 questions.vllm bench serve1K input / 128 output completed 10/10 requests at concurrency 1 and 160/160 requests at concurrency 16, with no server errors. These serving runs used the untuned FlyDSL a16w4 fallback, so they are a functional check, not a tuned peak.Long-context and DSpark validation against this AITER release remains pending.
Dependency and scope
ROCm/aiter#4646 is in AITER v0.1.21.post2. vLLM picked that tag up in #55968, so this branch is no longer blocked on the AITER release.
The conversion is an explicit opt-in for deployments that accept a lossy one-time weight conversion. It does not change the default checkpoint path.
Test plan