Skip to content

[ROCm][Perf] Kimi-K3: token-sharded residual stream for long prefills - #59693

Open
vanshbhatia-amd wants to merge 12 commits into
vllm-project:mainfrom
vanshbhatia-amd:k3/prefill-sp
Open

vanshbhatia-amd wants to merge 12 commits into
vllm-project:mainfrom
vanshbhatia-amd:k3/prefill-sp

Conversation

@vanshbhatia-amd

@vanshbhatia-amd vanshbhatia-amd commented Oct 1, 2026 •

Copy link
Copy Markdown
Contributor

Purpose

Scope: ROCm only, Kimi-K3 only, opt-in. All model changes are in
vllm/models/kimi_k3/amd/, which is only loaded on ROCm, and the new path only
runs for Kimi-K3's attention-residual layers. It is off by default
(VLLM_KIMI_K3_AMD_PREFILL_SP_MIN_TOKENS=0). NVIDIA, other models and
CUDA-graph decode are unaffected.

With TP, every rank repeats the same per-token work (attention-residual mixing,
MoE router, latent down-projection, top-k) on all tokens. For eager steps with
at least VLLM_KIMI_K3_AMD_PREFILL_SP_MIN_TOKENS tokens (1024 recommended),
this PR shards the residual stream over tokens instead, padded to a multiple of
TP:

  • Each block output all-reduce becomes a reduce-scatter plus an all-gather
    before the next block. For attention and dense MLP blocks the communication
    volume is unchanged. Latent-MoE layers also all-gather the latent and the
    top-k, about 17% more bytes per MoE layer at K3's sizes; the measured numbers
    below include this.
  • Attention-residual mixing, the router and top-k run on the local shard only.
  • The latent-MoE tail reduce-scatters its output over tokens instead of
    all-reducing it.

It is off with PP, with DP > 1, during graph capture and specialized-decode
steps, when aux hidden states
are requested (EAGLE drafters), and with --enable-return-routed-experts or
EPLB, since routing would only see this rank's token shard.

The NVIDIA Kimi-K3 path has something similar (use_sequence_parallel in
nvidia/model.py), but it only turns on with expert parallelism plus DP > 1
or DeepGEMM MegaMoE, for every step. This PR covers plain TP on ROCm, and only
for long prefill steps.

Test Plan

  • New tests/models/kimi_k3/test_amd_prefill_sp.py:
    • The MoE tail, regular and token-sharded, matches the replicated result at
      TP4/TP8 for both the ColumnParallelLinear and replicated up-projection
      layouts.
    • The decoder layer's sharded MLP step matches the unsharded rows at TP4 for
      a dense KimiMLP, a shard-routed latent KimiMoE, and a latent KimiMoE
      that takes the all-gather fallback, with a token count that does not divide
      TP.
    • The routing all-gather and the gather of pre-quantized FP8 activations
      (data plus per-token scale) are bit-exact, padded gather/scatter
      round-trips at TP4/TP8, and the enable conditions are covered.
  • With the existing Kimi-K3 AMD runner, KDA/MLA direct-return and sequence
    parallel tests: 78 passed on vllm/vllm-openai-rocm:nightly (vllm 5555a2d).
    After merging main, with the attention-residual tests added, 94 pass on the
    merged tree.
  • gfx942, same image: the unit tests above pass on 8x MI300X (78). On 8x
    MI325X, the full Kimi-K3 MXFP4 checkpoint at TP8 matches stock on GSM8K 5/20-shot
    and passkey, with the same first-token top-1 on 20/20 prompts up to 190K tokens,
    and cuts TTFT by 5-9% (MoE runs through the EMULATION MXFP4 backend there, so
    absolute numbers are not representative).
  • End to end: 8x MI355X, Kimi-K3 MXFP4, TP8. Both arms use the same
    vllm/vllm-openai-rocm:nightly image (vllm 5555a2d); the PR arm applies
    this PR's diff to the image's vllm and sets the flag. The agentic TP8 + DCP8
    config (prefix caching, CPU KV offload, 24576 batched tokens) also serves
    160/160 requests with and without the flag.

Serve (the PR arm adds -e VLLM_KIMI_K3_AMD_PREFILL_SP_MIN_TOKENS=1024):

docker run --rm --device=/dev/kfd --device=/dev/dri --group-add video --group-add render \
  --security-opt seccomp=unconfined --ipc=host --shm-size=128g --network host \
  -e HSA_NO_SCRATCH_RECLAIM=1 -e HIP_FORCE_DEV_KERNARG=1 -e SAFETENSORS_FAST_GPU=1 \
  -e VLLM_ROCM_USE_AITER=1 -e VLLM_ROCM_USE_AITER_MOE=1 \
  vllm/vllm-openai-rocm:nightly \
  vllm serve <Kimi-K3> --served-model-name k3 --trust-remote-code \
    --tensor-parallel-size 8 --max-model-len 204800 --max-num-seqs 32 \
    --gpu-memory-utilization 0.95 --no-enable-prefix-caching

Benchmark (one warmup run first; N = max(2 * C, 4)):

vllm bench serve --backend vllm --model <Kimi-K3> --served-model-name k3 \
  --trust-remote-code --port 8000 --dataset-name random \
  --random-input-len $ISL --random-output-len 1000 --random-range-ratio 0 \
  --max-concurrency $C --num-prompts $N --ignore-eos --seed 42 \
  --percentile-metrics ttft,tpot,itl,e2el

Accuracy, against /v1/completions with temperature 0 on the same servers:

  • GSM8K: the first 400 test questions, with a k-shot prefix built from the
    first k train examples as Question: ...\nAnswer: ...\n\n. Settings are
    max_tokens 320 and stop on Question: or three newlines, and the last number
    is compared with the #### answer. 5-shot prompts are only about 550 tokens,
    so they are sent in synchronized waves of 32 to put every prefill step above
    the threshold. 20-shot prompts (over 2K tokens) are sent at concurrency 32.
    Each is run 3 times per arm.
  • Passkey: a 5-digit key in filler text at depths 0.1/0.5/0.9 of
    8K/50K/100K/190K-token prompts (12 cases, 12 output tokens), plus 20 cases at
    depths 0.1-0.9 (48 output tokens).
  • First-token top-1: 20 real-text prompts (concatenated GSM8K train Q/A, 4
    offsets at each of 2K/10K/50K/100K/190K tokens), sent one at a time with
    logprobs=5, run twice per arm.

Test Result

At this head, 2026-10-20, stock → PR. Throughput is (input + output)
tok/s/GPU; TTFT and TPOT are means.

Input Conc. Throughput TTFT TPOT
10K 32 939 → 1046 (+11.4%) 7.97 → 6.02 s (-24.4%) 38.8 → 36.0 ms (-7.3%)
50K 1 331 → 338 (+2.1%) 2.98 → 2.70 s (-9.6%) 16.3 → 16.2 ms (-0.7%)
50K 2 553 → 573 (+3.6%) 4.81 → 4.34 s (-9.8%) 18.2 → 17.9 ms (-1.8%)
50K 4 832 → 864 (+3.9%) 7.45 → 6.82 s (-8.4%) 23.2 → 22.7 ms (-2.2%)
50K 8 1169 → 1238 (+5.9%) 10.91 → 9.46 s (-13.3%) 32.7 → 31.7 ms (-3.0%)
50K 16 1463 → 1566 (+7.1%) 16.00 → 15.11 s (-5.6%) 53.6 → 49.9 ms (-6.9%)
50K 32 1661 → 1803 (+8.5%) 27.12 → 24.77 s (-8.6%) 95.2 → 87.9 ms (-7.7%)
200K 8 1475 → 1588 (+7.7%) 44.49 → 40.80 s (-8.3%) 91.4 → 85.4 ms (-6.6%)

TPOT drops because decode steps that share a batch with a long prefill finish
sooner.

Accuracy Stock PR
GSM8K 5-shot, mean of 3 0.984 0.978
GSM8K 20-shot, mean of 3 0.987 0.986
Passkey, 12 cases up to 190K 10/12 10/12
Passkey, 20 cases 20/20 20/20
First-token top-1 vs stock 19/20 (run to run) 39/40

The GSM8K differences are within run-to-run noise: two runs of the same arm
disagree on 7-10 of the 400 answers, and PR vs stock runs on 6-11 (mean 8.7).
On the agentic config at this head, 5-shot is 0.987 for both. The two passkey
misses are the same 8K cases on both arms, where the model answers
conversationally instead of with the key. The one top-1 miss is a 50K prompt
whose top-1 also differs between two stock runs.

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Claude Code Review

This pull request is from a fork — automated review is disabled. A repository maintainer can comment @claude review to run a one-time review.

@mergify mergify Bot added kimi k3 rocm Related to AMD ROCm labels Oct 1, 2026
@github-project-automation github-project-automation Bot moved this to Todo in AMD Oct 1, 2026
Signed-off-by: Vansh Bhatia <vansh.bhatia@amd.com>
Both up-projection tails folded the shared output in with addmm_. On ROCm,
hipBLASLt's C-accumulating bf16 GEMM faults at some row counts for these
shapes: 2914-2925 rows per rank at 7168x3584 in the token-sharded tail (a
~23.4K-token prefill step at TP8), and bands from ~16.5K tokens at 896x3584
in the existing tail. Use a plain GEMM plus an add, which costs about 10 us
per MoE layer at 3K rows.

Signed-off-by: Vansh Bhatia <vansh.bhatia@amd.com>
Comment thread vllm/models/kimi_k3/amd/latent_moe_runner.py Outdated
Comment thread vllm/models/kimi_k3/amd/linear.py Outdated
Comment thread vllm/models/kimi_k3/amd/linear.py
Comment thread tests/models/kimi_k3/test_amd_prefill_sp.py Outdated
Comment thread vllm/models/kimi_k3/amd/sp.py
Comment thread vllm/models/kimi_k3/amd/linear.py Outdated
Comment thread vllm/models/kimi_k3/amd/latent_moe_runner.py Outdated
- Reuse the column-parallel up-projection tail and reduce-scatter its
  output over tokens, so the path works with the row-sharded up_proj.
- Gather pre-quantized AttnRes outputs as data plus per-token scale and
  rebuild them, keeping the FP8 input fusion.
- Pass the sharded token count to the decoder layers and the MoE runner
  as arguments instead of module globals.
- Only shard with DP == 1.
- Drop the addmm_ workaround; the hipBLASLt fault it avoided only
  reproduces on the ROCm 10 preview image.
- Test the ColumnParallelLinear up_proj layout and the quantized gather.

Signed-off-by: Vansh Bhatia <vansh.bhatia@amd.com>

@kliuae kliuae left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the turnaround. Looks good in general, just have some nits on setting reduce_results and test coverage.

As the latent is now all-reduced, instead of the reduce scatter before syncing in the upstream changes. Could you remeasure the performance numbers to check if the performance lift is still intact?

out = self._run_self_attn(positions, hidden_states)
return sp.scatter_tokens(out)

def _run_moe_sp_shard_routed(self, hidden_shard: torch.Tensor) -> torch.Tensor:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pre_routed depends on select_experts being exercised so that its output can be replaced. Is it possible to make sure that it's used, maybe adding a test that builds a small Kimi MoE module and compare the shard results with what un-sharded gets on the same rows, to make sure that this path doesn't route on the uninitialized logits?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added both, in adb2392. sp.pre_routed now asserts that the MoE forward called select_experts exactly once, so a backend that routes from router_logits directly, or routes per chunk, fails loudly instead of reading the placeholder logits.

test_shard_routed_moe_tp{4,8}_matches_unsharded_rows builds a small latent KimiMoE (column-parallel up-projection, a shared expert, a sigmoid router with correction bias and grouped top-k). It runs the layer unsharded on all tokens and through _run_moe_sp_shard_routed on each rank's token shard, and checks that each rank's output matches its rows of the unsharded result. It is parametrized over the Triton and AITER MoE backends. The production MXFP4 + AITER path also passes the assert end to end (TP8 + DCP8 serving run, 160/160 requests).

Comment thread vllm/models/kimi_k3/amd/linear.py Outdated
)

@contextlib.contextmanager
def _output_unreduced(self, module: nn.Module):

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This forces reduce_results=False for all RowParallelLinear modules. Even though self_attn and mlp contains only the intended o-proj and down_proj as RowParallelLinear, for readability, can we make this more targeted for the intended modules? Something like

@staticmethod
@contextlib.contextmanager
def _output_unreduced(proj: RowParallelLinear):
    # Row-parallel outputs stay partial so the caller can reduce-scatter.
    assert isinstance(proj, RowParallelLinear) and proj.reduce_results
    proj.reduce_results = False
    try:
        yield
    finally:
        proj.reduce_results = True

then call it with self.self_attn_o_proj and self.mlp.down_proj

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done as suggested: _output_unreduced is now a staticmethod that takes the one RowParallelLinear and asserts it, called with self.self_attn.o_proj and self.mlp.down_proj.

…rojs

- pre_routed now asserts the MoE forward routes exactly once, so the
  placeholder router logits can never be read.
- Test a small latent KimiMoE: routing on the token shard returns this
  rank's rows of the unsharded layer at TP4/TP8, on Triton and AITER.
- _output_unreduced takes the one RowParallelLinear to leave partial
  (attention o_proj, MLP down_proj) instead of every one in the module.

Signed-off-by: Vansh Bhatia <vansh.bhatia@amd.com>
@vanshbhatia-amd

Copy link
Copy Markdown
Contributor Author

Thanks for the review! adb2392 addresses the two nits.

On performance: the Test Result table in the description was re-measured on 2026-10-14, after the switch to the all-reduced latent, with the same nightly image for both arms. It shows +4.4% / +8.5% / +7.4% throughput and -15% / -10% / -10% TTFT at 10K/32, 50K/32 and 200K/8. The previous revision measured +5.7% / +11.1% / +9.3% at those points, so dropping the latent reduce-scatter costs about 1-2 points and most of the lift is intact.

@kliuae kliuae left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, and thanks for updating the performance numbers

Comment thread vllm/models/kimi_k3/amd/sp.py
Comment thread tests/models/kimi_k3/test_amd_prefill_sp.py
Comment thread vllm/models/kimi_k3/amd/linear.py
Comment thread vllm/models/kimi_k3/amd/sp.py Outdated
…e sharded MLP step

Routed-experts capture and EPLB load recording see routing from
select_experts, which under SP only runs on this rank's token shard, so
should_shard now also requires neither to be enabled.

Tests: TP4 padded gather/scatter, a token count that does not divide TP in
the shard-routed MoE check, and _run_mlp_sp against the unsharded rows for a
dense KimiMLP and a non-token-shardable KimiMoE. The sp.py docstring now
notes the extra latent and top-k all-gathers in latent-MoE layers.

Signed-off-by: Vansh Bhatia <vansh.bhatia@amd.com>

@xaguilar-amd xaguilar-amd left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice work!

Some comments/suggestions:

  • The token-sharded path is not gated on gfx950 so could you add gfx942 checks on the current head, same image with the flag off and with VLLM_KIMI_K3_AMD_PREFILL_SP_MIN_TOKENS=1024? Just to be sure that everything stays healthy there too. Please include the commands.

  • Accuracy in the description is from the previous revision. Please rerun it at this head, again with the same image on both arms and the flag set to 1024: GSM8K 5-shot, passkey out to ~190K, and the first-token top-1 check. Put the serve and eval commands in the description. A 20-shot case would be nice to have as well and would make the PR stronger.

  • For the MI355X sweep, the 2026-10-14 table is three high-concurrency points and reports throughput and TTFT only. The 8-point sweep is the earlier revision. On this head, could you add a few more concurrencies at one of the existing input lengths (10K or 50K), keep the 10K/32, 50K/32, and 200K/8 points to see how the code does there? Please report TPOT as well since it's an important metric. Same nightly image for the base and the PR, with the serve and vllm bench serve commands in the description.

@vanshbhatia-amd

vanshbhatia-amd commented Oct 20, 2026 •

Copy link
Copy Markdown
Contributor Author

Thanks! Reran both at this head (f0c6a4b), same vllm/vllm-openai-rocm:nightly image on both arms, flag at 1024. The description now has the serve, vllm bench serve and eval commands.

Accuracy, stock vs PR (GSM8K is 400 questions, mean of 3 runs):

Check Stock PR
GSM8K 5-shot 0.984 0.978
GSM8K 20-shot 0.987 0.986
Passkey, 12 cases up to 190K 10/12 10/12
Passkey, 20 cases 20/20 20/20
First-token top-1 vs stock 19/20 (run to run) 39/40

The 5-shot gap is run-to-run noise: two runs of the same arm already disagree on 7-10 of the 400 answers, and PR vs stock runs on 6-11. The two passkey misses are the same 8K cases on both arms. The one top-1 miss is a 50K prompt whose top-1 also differs between two stock runs.

Sweep: kept 10K/32, 50K/32 and 200K/8 and added 50K at concurrency 1, 2, 4, 8 and 16, with TPOT. 8x MI355X, TP8, 1K output tokens, stock → PR. Throughput is (input + output) tok/s/GPU; TTFT and TPOT are means.

Input Conc. Throughput TTFT TPOT
10K 32 939 → 1046 (+11.4%) 7.97 → 6.02 s (-24.4%) 38.8 → 36.0 ms (-7.3%)
50K 1 331 → 338 (+2.1%) 2.98 → 2.70 s (-9.6%) 16.3 → 16.2 ms (-0.7%)
50K 2 553 → 573 (+3.6%) 4.81 → 4.34 s (-9.8%) 18.2 → 17.9 ms (-1.8%)
50K 4 832 → 864 (+3.9%) 7.45 → 6.82 s (-8.4%) 23.2 → 22.7 ms (-2.2%)
50K 8 1169 → 1238 (+5.9%) 10.91 → 9.46 s (-13.3%) 32.7 → 31.7 ms (-3.0%)
50K 16 1463 → 1566 (+7.1%) 16.00 → 15.11 s (-5.6%) 53.6 → 49.9 ms (-6.9%)
50K 32 1661 → 1803 (+8.5%) 27.12 → 24.77 s (-8.6%) 95.2 → 87.9 ms (-7.7%)
200K 8 1475 → 1588 (+7.7%) 44.49 → 40.80 s (-8.3%) 91.4 → 85.4 ms (-6.6%)

The PR is ahead at every point, +6.2% geomean throughput. The gain grows with concurrency at 50K, since more prefill tokens per step are sharded. TPOT drops because decode steps that share a batch with a long prefill finish sooner.

@vanshbhatia-amd

Copy link
Copy Markdown
Contributor Author

On gfx942: 8x MI300X, same vllm/vllm-openai-rocm:nightly image (pulled by digest sha256:e0be00f1...), PR applied.

Unit tests: the PR's Kimi-K3 AMD tests (prefill SP, latent-MoE runner, KDA/MLA direct return, sequence parallel), including the TP4/TP8 multi-GPU cases: 78 passed.

End to end: the MXFP4 Kimi-K3 checkpoint is ~1.5 TB, which doesn't fit in 8x 192 GB, so this uses Kimi-K3's config reduced to 24 layers (two attention-residual blocks; full attention every 4th layer, KDA elsewhere) and 64 experts, in bf16 with seeded dummy weights, flag off vs 1024. The dummy init was rescaled for the test (norm weights 1 ± 0.1, matrices with variance 1/fan_in), because the default ±1e-3 init gives uniform logits. Both arms use Triton MoE (VLLM_ROCM_USE_AITER_MOE=0): with aiter MoE, the bf16 CK MoE kernel for SiTU doesn't build on gfx942, and boot fails with the flag off too.

Check Flag off vs 1024 Same arm, run to run
Boot and serve, faults or errors none on either arm —
Max first-token |Δ logprob|, single prompts (1K–16K) 0.095 0 (bit-identical)
Max first-token |Δ logprob|, 16 concurrent 3K prompts 0.125 0.094

Every prefill step was above the threshold (no step between 33 and 1024 tokens), so the sharded path ran. Top-1 isn't a useful metric with random weights, since the top two logprobs are exact bf16 ties on most prompts.

Commands:

# unit tests
pytest -q tests/models/kimi_k3/test_amd_prefill_sp.py tests/models/kimi_k3/test_amd_latent_moe_runner.py \
  tests/models/kimi_k3/test_amd_kda_direct_return.py tests/models/kimi_k3/test_amd_mla_direct_return.py \
  tests/models/kimi_k3/test_sequence_parallel.py
# serve, reduced config; VLLM_KIMI_K3_AMD_PREFILL_SP_MIN_TOKENS=0 or 1024
VLLM_ROCM_USE_AITER=1 VLLM_ROCM_USE_AITER_MOE=0 VLLM_KIMI_K3_AMD_PREFILL_SP_MIN_TOKENS=$SP \
vllm serve <Kimi-K3, 24 layers, 64 experts, no quantization_config> --served-model-name k3 \
  --trust-remote-code --tensor-parallel-size 8 --load-format dummy --language-model-only \
  --max-model-len 32768 --max-num-seqs 32 --gpu-memory-utilization 0.9 --no-enable-prefix-caching

The probe sends 20 random-token prompts of 1K–16K one at a time, then 16 concurrent 3K-token prompts, greedy with 16 output tokens and logprobs=5, twice per server.

@mergify

mergify Bot commented Oct 21, 2026

Copy link
Copy Markdown
Contributor

This pull request has merge conflicts that must be resolved before it can be
merged. Please rebase the PR, @vanshbhatia-amd.

https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/syncing-a-fork

@mergify mergify Bot added the needs-rebase label Oct 21, 2026
Signed-off-by: Vansh Bhatia <vansh.bhatia@amd.com>
@vanshbhatia-amd

Copy link
Copy Markdown
Contributor Author

Follow-up on gfx942 with the full model: 8x MI325X (256 GB), the real Kimi-K3 MXFP4 checkpoint, TP8, same image (vllm/vllm-openai-rocm by digest sha256:e0be00f1...), stock vs PR applied with VLLM_KIMI_K3_AMD_PREFILL_SP_MIN_TOKENS=1024. Serve flags are the ones in the description (--max-model-len 204800, aiter on).

On gfx942 this image runs Kimi-K3's MXFP4 MoE through the EMULATION backend (the only other gfx942 path is the opt-in int4 requant, which is lossy), so decode is slow and the absolute numbers are not representative. The perf points use 64 output tokens so they mostly measure prefill.

Accuracy Stock PR
GSM8K 5-shot, 400 questions 0.9875 0.9825
GSM8K 20-shot, 400 questions 0.9925 0.9900
Passkey, 12 cases up to 190K 10/12 10/12

Single runs; the GSM8K differences are smaller than the run-to-run spread on MI355X. The passkey misses are the same conversational answers as on MI355X.

First token, 20 prompts at 2K–190K Stock vs stock PR vs PR PR vs stock
Top-1 same 20/20 20/20 20/20
Max |Δ logprob| 0 (bit-identical) 0 (bit-identical) 0.097
Max mean top-5 KL (per length) 1.7e-6 0 5.0e-3
32-token greedy output identical 18/20 18/20 16/20
Input Conc. TTFT, stock → PR Throughput
10K 32 17.11 → 16.09 s (-6.0%) +4.1%
50K 1 8.40 → 7.94 s (-5.5%) +1.5%
50K 8 25.79 → 24.30 s (-5.8%) +4.4%
50K 32 140.2 → 127.2 s (-9.3%) +6.3%
200K 8 188.0 → 179.0 s (-4.8%) +4.8%

Separately, I merged main (3acec0a). The only conflict was the layer loop in KimiLinearModel.forward with the specialized decode step from #60683: a planned kernel runs as before, otherwise the layer gets sp_num_tokens, and SP is now also off whenever a specialized-decode step is planned. On the merged tree, ruff is clean and the PR's tests plus the existing Kimi-K3 AMD runner, KDA/MLA direct-return, sequence-parallel and attention-residual tests pass (94).

@mergify mergify Bot removed the needs-rebase label Oct 21, 2026
@xaguilar-amd

Copy link
Copy Markdown
Contributor

@vanshbhatia-amd thanks for the extra experiments!

I cannot see the serve commands on the description as you mentioned though.

@vanshbhatia-amd

Copy link
Copy Markdown
Contributor Author

@xaguilar-amd sorry, they're in the Test Plan section of the description ("Serve (the PR arm adds ..."), but here are the exact MI325X ones.

Serve, both arms (the PR arm first applies the PR's diff to the image's vllm, git diff 9b5c6707a f0c6a4bb0 -- vllm/ with patch -p1 in site-packages, and adds -e VLLM_KIMI_K3_AMD_PREFILL_SP_MIN_TOKENS=1024):

docker run --rm --device=/dev/kfd --device=/dev/dri --group-add video --group-add render \
  --security-opt seccomp=unconfined --ipc=host --shm-size=128g --network host \
  -v <Kimi-K3>:<Kimi-K3>:ro \
  -e HSA_NO_SCRATCH_RECLAIM=1 -e HIP_FORCE_DEV_KERNARG=1 -e SAFETENSORS_FAST_GPU=1 \
  -e VLLM_ROCM_USE_AITER=1 -e VLLM_ROCM_USE_AITER_MOE=1 \
  vllm/vllm-openai-rocm@sha256:e0be00f1b99da608cc3c8571c7c86bae0c05f19e1e61a59c1d70aeea6bca6140 \
  vllm serve <Kimi-K3> --served-model-name k3 --trust-remote-code \
    --tensor-parallel-size 8 --max-model-len 204800 --max-num-seqs 32 \
    --gpu-memory-utilization 0.95 --no-enable-prefix-caching

On gfx942 the log shows Using 'EMULATION' Mxfp4 MoE backend.

Benchmark (one warmup first; N = max(2 * C, 4); (ISL, C) in (10000, 32), (50000, 1), (50000, 8), (50000, 32), (200000, 8)):

vllm bench serve --backend vllm --model <Kimi-K3> --served-model-name k3 \
  --trust-remote-code --port 8000 --dataset-name random \
  --random-input-len $ISL --random-output-len 64 --random-range-ratio 0 \
  --max-concurrency $C --num-prompts $N --ignore-eos --seed 42 \
  --percentile-metrics ttft,tpot,itl,e2el

GSM8K, passkey and the first-token prompts are the same requests as described under "Accuracy" in the description, against /v1/completions on these servers.

@vanshbhatia-amd

Copy link
Copy Markdown
Contributor Author

Hi @dllehr-amd @hongxiayang @tjtanaa - would love for a review on this! Thanks!

@okorzh-amd

Copy link
Copy Markdown
Contributor

LGTM

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

k3 kimi rocm Related to AMD ROCm

Projects

Status: Todo

Development

Successfully merging this pull request may close these issues.

4 participants