Repository navigation
[Perf] [AMD] Kimi-K3: prepare the attention interface for mono fusion - #45202
Merged
Merged
Conversation
billishyahao
force-pushed
the
k3_mono_new
branch
5 times, most recently
from
October 21, 2026 17:12
b58fbfa to
bff7cd6
Compare
billishyahao
force-pushed
the
k3_mono_new
branch
from
October 21, 2026 17:40
bff7cd6 to
d6ea5fc
Compare
billishyahao
marked this pull request as ready for review
October 21, 2026 18:01
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
Collaborator
Author
|
base test all passed. https://github.com/sgl-project/sglang/actions/runs/39700405113 cc @HaiShaw |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
billishyahao
added a commit
to sgl-project/sgl-amd-mono
that referenced
this pull request
Oct 23, 2026
A Kimi-K3 decode or DSpark verify step runs as two persistent launches a layer. K1 runs the attention side of a KDA layer: the attention-side residual seam, input_layernorm, the in-projection, f_b, the conv, the recurrence and the gated output norm. It covers the 69 KDA layers. K2 takes the TP-local attention output from there and runs o_proj, the attention-TP all-reduce, the MLP attention-residual seam and the MoE. It covers all 92 MoE layers, from K1 on a KDA layer and from SGLang's own attention on a full-attention layer. Both reduces go over a peer buffer. Neither leaves the kernel. TP8, gfx950, 1 to 8 rows a step, which is what MAX_ROWS is. DSpark block 7 gives one request of 8 tokens. No weight copies. The dense weights are read as bf16 in their checkpoint layout, the experts in whichever MXFP4 layout Mxfp4MoEMethod left behind. common gains what the MoE stage needs and nothing else: the wave reductions, the CTA scan, the mailbox region shift, and KernelAbi. Two deliberate differences from DeepSeek-V4.1, each argued where it lives. The host bumps the mailbox epoch and a launch carries its own slot, instead of advancing on the device. The build compiles by running each launch once on real weights, not through FlyDSL's compile-only mode, whose zero arguments fold the buffer loads into a pattern LLVM cannot select. Tests: test/unit covers the spec, its bounds, the per-layer refusals and a source-digest drift guard on the five SGLang functions the forward reproduces. test/kernels covers the layer launch's three outputs against SGLang's ops, the MoE launch on both expert layouts, a bitwise graph replay, and K1 on one GPU. test/e2e covers serving accuracy, KL and fault injection. Needs sgl-project/sglang#45202. Without the KDA in-projection merge _qkvgbfa_layer is None and K1 builds for no layer; without forward_pre_o_proj test_contract.py fails and the spec refuses at model load.
5 tasks done
Collaborator
Author
|
@HaiShaw @kkHuang-amd new round of basic test is passed. It shows no side effect on common path: https://github.com/sgl-project/sglang/actions/runs/41613665770
|
Collaborator
|
@billishyahao can you bump the sgl-amd-mono commit# reference. |
Collaborator
Author
Thanks @HaiShaw for the reminding! I bump the sgl-amd-mono commit through 254ce94 |
This was referenced Nov 5, 2026
This was referenced Nov 5, 2026
jhinpan
added a commit
to jhinpan/sglang-fork
that referenced
this pull request
Nov 6, 2026
…he GLM-5.3 agentic recipe Section 3.6 now names lmsysorg/sglang-rocm:v0.5.23-rocm720-mi35x-20261105, the first MI35x image whose SGLang pins sgl-amd-mono e52803f9 (sgl-project#45202), which contains sgl-project/sgl-amd-mono#85 and sgl-project#86.
5 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

This PR co-work with mono patch: sgl-project/sgl-amd-mono#13
Motivation
The AMD monokernel backend runs a Kimi-K3 decode or DSpark verify step as two persistent launches a layer. They run in this order.
input_layernorm, the in-projection,f_b, the conv, the recurrence and the gated output norm. It covers the 69 KDA layers. The 24 full-attention layers keep stock SGLang's attention instead.That backend is sgl-project/sgl-amd-mono. It loads through the
sglang.srt.pluginsentry point and is selected with--monokernel-backend amd. SGLang needs nothing else for it:install_monokernel_forwardswaps the whole model forward (srt/monokernels/runtime.py), so the kernels, the spec and its registration all live in the plugin. This method is the one thing the plugin cannot reach from outside, which is why it is the only change here. In short, this change follows the sgl mono kernel RFC #43949Modifications
1. The KDA in-projection gate, which K1 reads. Take the merge when
do_fuse_qkvbfgallows it, as before, or when a monokernel backend is selected:With no backend selected this reduces to
use_full_rank_gate and do_fuse_qkvbfg, which is what the predicate tested before. That identity is deliberate. The fused GEMM is a different kernel from the split pair. A wider gate on its own would move decode output by one bf16 rounding step for every ROCm Kimi-K3 user. The file's own docstring records that the two paths agree to bf16 rounding and not bitwise.The weight checks below the gate do not change, and they still decide. The backend changes the policy. It does not remove the safety checks.
_is_unquantized_mergeablerequires the three weights to share one dtype, and that dtype to be bf16 or fp16. A per-channel FP8 weight or a packed MXFP4 weight still refuses, because_merge_weights_as_viewsconcatenates.weightalone.Nothing else changes.
do_fuse_qkvbfgalso gates a weight-loader branch and the low-rank gate's projection layout. This commit touches neither.2.
forward_pre_o_proj()onKimiK3DeltaAttentionandKimiK3MLAAttention, where K2 starts. It returns the TP-local attention output after the output gate and before o_proj. The caller owns o_proj and the attention-TP reduction that o_proj carries, soo_proj(forward_pre_o_proj(x)) == forward(x).KimiK3DeltaAttention: a plain extract.forward()now calls the new method and keeps the o_proj tail. The moved body does not change.KimiK3MLAAttention: the MLA cores reach o_proj from several places. The split therefore uses the instance-level wrapper that already applies the output gate.forward_pre_o_proj()sets_return_pre_o_proj, runsforward(), and takes the tensor that the wrapper puts in_pre_o_proj_outinstead of running o_proj.Four guards hold the MLA path together. It has no in-tree caller to exercise it, and a fault on it gives wrong numbers rather than an error.
use_output_gatemust be on. Without it the wrapper does not exist._pre_o_proj_outholds one tensor. If o_proj is reached twice in one attention forward, the second call raises instead of overwriting the first. The message names the layer.forward_mla_rocm.py:325,351). The output gate is skipped with it, so there is no gated bf16 tensor to hand back. Every rank quantizes alike, so this raises on all of them. It does not split the TP group.The standard path remains no change.
forward()produces what it produced before if no mono kernel server args are specified.Accuracy Tests
8 x MI355X, TP8 / DCP1, FP8 KV cache, DSpark block size 7.
/data-models/Kimi-K3, GSM8K 200 questions,--parallel 2:--monokernel-backend amdSpeed Tests and Profiling
--monokernel-backend amdagainst the standard path, both launches on. Twoserver instances an arm, four probe runs each, the first run of each server
dropped. Decode steps/s rather than tok/s, because the speculative accept
length swamps tok/s on this recipe.
The ranges do not overlap. The win is on 8-row verify steps; wider steps and
all prefill take the standard path.
Fixed-length decode latency at long context
The table above is short prompts at the draft's real acceptance. This one is the interactive shape the backend is for: a cached long prefix, a small new turn, and a short answer. It also reports the inter-token latency the backend moves, rather than a step rate that a reader has to convert.
off/onis--monokernel-backend amd. Lower ITL is better, so Δ is the reduction; as a speedup the same numbers are +33.7% / +25.9% / +24.3% at P50.P90 moves with P50 (−24.0% against −25.2% at 64k) and the P90/P50 ratio is unchanged (1.31 off, 1.33 on). The launch shifts the whole distribution rather than clipping a tail. The three columns agree because acceptance is pinned:
ITL ~= 1000 / (steps/s * accept).The gain falls as ISL grows (−25.2% to −19.6%). Attention over a longer KV takes a larger share of the step, and that part is outside the launch.
TTFT is unchanged -- 0.24/0.25 s, 0.30/0.29 s and 0.41/0.41 s at 64k/128k/256k. The launch covers decode only.
End-to-end agentic replay
The two tables above are synthetic prompts at a pinned acceptance. This one is a full AgentX agentic-coding replay, one hour per arm. It therefore carries real session content, real prefix-cache behaviour and the batching of the scheduler itself.
--monokernel-backend amdEvery percentile moves by the same amount, −19.8% to −21.3%, as in the fixed-length table. Throughput rises less than latency falls, +14.9% against −21.3%. AgentX replays a closed loop, so a faster answer shortens only the part of each session's cycle that the engine owns.
The number of steps the backend can take bounds the gain. An earlier pair of the same runs admitted two requests instead of one. That pair held the SSM state in bf16, so read its percentages against its own baseline and not against the table above. There 6 to 7% of decode windows held two running requests. That is 2 x 8 = 16 rows, which is outside the spec, and the result split: P50 −16.5%, but P90 −2.6% and P95 −2.1%, with the mean diluted to −9.4%. The server log shows it directly when read by
#running-req: the one-request steps ran 9.8% faster and the two-request steps 2.2%. Admitting one request removes the steps that are not covered, and the whole distribution then moves together. A caller that wants the full gain must keep every verify step inside the supported width.Checklist
Review and Merge Process
/tag-and-rerun-ci,/tag-run-ci-label,/rerun-failed-ciCI States
Latest PR Test (Base): ⏳ Run #41977697297
Latest PR Test (Extra): ❌ Run #41977697285
Latest PR Test (AMD ROCm 10): ⏳ Run #41977697327
Tip
Base CI only covers the core cases, and its workload may not exercise your change. Use
/rerun-testto run the tests your change touches, or copy this prompt for an agent: