Skip to content

[NPU] support kimi k3 on A5 and improve performance - #39589

Merged
sglang-npu-bot merged 40 commits into
sgl-project:mainfrom
zhaozx-cn:a5-k3-0915
Sep 18, 2026
Merged

sglang-npu-bot merged 40 commits into
sgl-project:mainfrom
zhaozx-cn:a5-k3-0915

Conversation

@zhaozx-cn

@zhaozx-cn zhaozx-cn commented Sep 15, 2026 •

Copy link
Copy Markdown
Contributor

Co-Authored-By: hanwlax
Co-Authored-By: Hexq0210
Co-Authored-By: McZyWu
Co-Authored-By: qybnb
Co-Authored-By: sherdavincl9

Motivation

support kimi k3 on A5 and improve performance.

Modifications

1.add compressed w4a8 mxfp4 moe.
2.shared expert: add fine-grained dual steam and support shared expert specified tp size.
3.add fused qkvg proj.
4.add situ mx quant kernel.
5.add kv nz for k3 mla.
6.add fia v2 for mtp branch.
7.add chunk kda kernel for prefill branch.
8.fix kimi k3 dspark acc pd disaggregation.

Accuracy Tests

#!/usr/bin/env bash
set -eo pipefail

NODE_RANK="$1"
source /usr/local/Ascend/ascend-toolkit/set_env.sh
source /usr/local/Ascend/nnal/atb/set_env.sh

export SGLANG_NPU_USE_FIAS_V2_BSND=True
export SGLANG_NPU_FINE_GRAINED_MOE_DUAL_STREAM=True

export SGLANG_ENABLE_OVERLAP_PLAN_STREAM=1
export SGLANG_ENABLE_SPEC_V2=1
export SGLANG_RAGGED_VERIFY_MODE=static
export SGLANG_DSPARK_FOLDED_PROPOSAL=0
export SGLANG_DSPARK_FOLDED_SAMPLING=0
export SGLANG_DSPARK_STACKED_CTX_KV=0
export SGLANG_DSPARK_EMBED_IN_GRAPH=0
export PYTORCH_NPU_ALLOC_CONF=expandable_segments:True
export HCCL_SOCKET_IFNAME=enp194s0f0
export GLOO_SOCKET_IFNAME=enp194s0f0
export STREAMS_PER_DEVICE=32
export DEEP_NORMAL_MODE_USE_INT8_QUANT=1
export SGLANG_DEEPEP_NUM_MAX_DISPATCH_TOKENS_PER_RANK=128
export HCCL_BUFFSIZE=2000
export DEEPEP_NORMAL_LONG_SEQ_ROUND=64
export DEEPEP_NORMAL_LONG_SEQ_PER_ROUND_TOKENS=512
export HCCL_OP_EXPANSION_MODE=AIV

unset ASCEND_CUSTOM_OPP_PATH
unset SGLANG_NPU_FUSED_MOE_MODE
unset ENABLE_PROFILING
unset SGLANG_K3_TRACE_STATE_FILE
unset SGLANG_K3_TRACE_HIDDEN_FILE

sglang serve \
  --model-loader-extra-config '{"enable_multithread_load": true}' \
  --dist-init-addr <<first node>>:30143 \
  --nnodes 4 \
  --node-rank "$NODE_RANK" \
  --model-path /home/weights/Kimi-K3 \
  --tokenizer-path /home/weights/Kimi-K3 \
  --trust-remote-code \
  --attention-backend ascend \
  --device npu \
  --quantization modelslim \
  --dtype bfloat16 \
  --tp-size 32 \
  --enable-dp-attention \
  --enable-dp-lm-head \
  --mem-fraction-static 0.9 \
  --chunked-prefill-size 8192 \
  --cuda-graph-bs 32 \
  --max-running-requests 32 \
  --host 0.0.0.0 \
  --enable-shared-experts-attn-tp \
  --enable-dense-mlp-attn-tp \
  --shared-experts-tp-size 4 \
  --port 30543 \
  --reasoning-parser kimi_k3 \
  --moe-a2a-backend deepep \
  --deepep-mode auto \
  --speculative-algorithm DSPARK \
  --speculative-draft-model-path /home/weights/Kimi-K3-DSpark \
  --speculative-draft-model-quantization unquant \
  --speculative-dspark-block-size 7 \
  --speculative-draft-attention-backend ascend \
  --linear-attn-verify-backend triton \
  --speculative-eagle-topk 1 \
  --disable-radix-cache \
  --disable-custom-all-reduce \
  --watchdog-timeout 9000
DeprecationWarning: sglang.test.few_shot_gsm8k is deprecated. Use sglang.test.run_eval with eval_name='gsm8k' instead.
  metrics = run_eval(args)
100%|████████████████████████████████████████████████████████| 200/200 [01:54<00:00,  1.74it/s]
Accuracy: 0.985
Invalid: 0.005
Latency: 116.842 s
Output throughput: 199.406 token/s
metrics={'accuracy': np.float64(0.98), 'invalid': np.float64(0.005), 'latency': 116.84225159999914, 'output_throughput': 199.405606113809}
metrics['accuracy']=np.float64(0.98)

Speed Tests and Profiling

128k in 1k out dspark 7 tp32 ep 32

Metric Value
Mean TFTT 5604.54 ms
Mean TPOT 23.02 ms
Throughput 47 tok/s

Checklist

Review and Merge Process

  1. Ping Merge Oncalls to start the process. See the PR Merge Process.
  2. Get approvals from CODEOWNERS and other reviewers.
  3. Trigger CI tests with comments or contact authorized users to do so.
    • Common commands include /tag-and-rerun-ci, /tag-run-ci-label, /rerun-failed-ci
  4. After green CI and required approvals, ask Merge Oncalls or people with Write permission to merge the PR.

CI States

Latest PR Test (Base): 🚫 Run #35314528985
Latest PR Test (Extra): ❌ Run #35314528785
Latest PR Test (AMD ROCm 10): ❌ Run #35314528997

zhaozx-cn and others added 24 commits September 15, 2026 05:53
Signed-off-by: zhaozx-cn <zhaozx2116@163.com>
Signed-off-by: zhaozx-cn <zhaozx2116@163.com>
…tion-TP all-gather and reduce-scatter on the current stream.
Signed-off-by: zhaozx-cn <zhaozx2116@163.com>
* fix(npu): use DeepEP MXFP8 dispatch for W4A8 MXFP MoE

* feat(npu): fuse K3 SiTU MXFP8 quantization

---------

Co-authored-by: zhaozx-cn <59479021+zhaozx-cn@users.noreply.github.com>
Replace the multi-kernel Ascend KDA extend path (chunk_local_cumsum,
chunk_kda_scaled_dot_kkt_fwd, solve_tril_npu, recompute_w_u_fwd_npu,
chunk_gated_delta_rule_fwd_h_npu, chunk_gla_fwd_o_gk_npu) with a single
torch.ops.npu.chunk_kda_fwd call.

The fused op takes one initial state per logical sequence in contiguous
[N, H, V, K] layout, while SGLang owns a slot-indexed persistent pool, so
the wrapper gathers the active slots before the call and scatters
final_state back afterwards. chunk_states comes back in the layout the
tracker already expects, which drops the transpose on the
intermediate-state path.

Forward metadata uses -1 in cache_indices for a padded request. The gather
would turn that into a read of the last cache slot, and index_copy_ would
then write the padded row's final_state back over that same slot,
corrupting the state of whichever live sequence owned it. Clamp the gather
indices to 0 so a padded row reads a harmless placeholder, and filter
padded rows out of the writeback entirely.

Drop the speculative-decoding-only NPU transpose of temporal_state in
MambaPool. It presented the pool as a transposed view so the stride-aware
verify kernel and state movers saw the canonical [pool, HV, V, K] logical
shape; the fused extend path consumes the pool directly in that layout, so
every consumer now agrees on one contiguous representation. The adjacent
shape comment described the swapped layout and is corrected with it.

Also rewrite the conv_states copy for non-tracked entries as a no-op
self-copy, so it no longer runs bool-mask indexing (aten::nonzero) or a
host numel check on every step.
Signed-off-by: zhaozx-cn <zhaozx2116@163.com>
Unpack PA-NZ latent and RoPE cache pages before prefix projection and attention concatenation. Ordinary MLA writes can use NZ independently of MLAPO, so raw page gathers otherwise mix token and feature dimensions.

Add CPU regression coverage for ND/NZ layouts, latent/RoPE dimensions, reordered and repeated pages, empty selections, and partial-page writes. Validated test bodies and forward_extend inputs in an isolated CPU PyTorch harness, including a legacy-path negative control; syntax and targeted Ruff checks passed. A5 GSM8K and NPU execution remain to be validated.
Signed-off-by: zhaozx-cn <zhaozx2116@163.com>
Signed-off-by: zhaozx-cn <zhaozx2116@163.com>
Signed-off-by: zhaozx-cn <zhaozx2116@163.com>
Signed-off-by: zhaozx-cn <zhaozx2116@163.com>
@github-actions github-actions Bot added the run-ci CI: run the baseline test suite on this PR label Sep 16, 2026
@zhaozx-cn

Copy link
Copy Markdown
Contributor Author

/rerun-failed-ci

Signed-off-by: zhaozx-cn <zhaozx2116@163.com>
Signed-off-by: zhaozx-cn <zhaozx2116@163.com>
@zhaozx-cn

Copy link
Copy Markdown
Contributor Author

/rerun-failed-ci

Signed-off-by: zhaozx-cn <zhaozx2116@163.com>
@zhaozx-cn

Copy link
Copy Markdown
Contributor Author

/rerun-failed-ci

1 similar comment
@zhaozx-cn

Copy link
Copy Markdown
Contributor Author

/rerun-failed-ci

@zhaozx-cn

Copy link
Copy Markdown
Contributor Author

/rerun-failed-ci

@sglang-npu-bot

sglang-npu-bot commented Sep 18, 2026 •

Copy link
Copy Markdown
Collaborator

Before commit 4ab4643, we had already passed all test cases. For details, please refer to...https://github.com/sgl-project/sglang/actions/runs/35219963083/job/105312395665?pr=39589. Commit 4ab4643 only modifies python/sglang/srt/environ.py to resolve conflicts. The evaluation does not affect functionality, so this PR is merged. If there are any issues, please leave a comment under this PR, and we will fix it as soon as possible. Thank you.

@sglang-npu-bot
sglang-npu-bot merged commit 8ac39c6 into sgl-project:main Sep 18, 2026
121 of 159 checks passed
gjsheu added a commit to gjsheu/sglang that referenced this pull request Sep 20, 2026
…rify

The NPU transpose in MambaPool's speculative state was removed in sgl-project#39589,
so on NPU the spec Mamba pool now hands out the untransposed `temporal`
tensor while the rest of the NPU verify path still works in the
[.., HV, K, V] convention:

  * AscendGDNAttnBackend / AscendHybridLinearAttnBackend reshape the
    per-draft scratch as (-1, num_value_heads, head_k_dim, head_v_dim);
  * move_intermediate_cache() (sgl_kernel_npu), which commits the state of
    the accepted prefix back into the live SSM state after target verify,
    walks each (heads, K, V) block with a hardcoded element order.

Handing out the untransposed tensor flips that order, so the state written
back after the first verify step is transposed. Everything generated after
it is garbage (word salad), while plain decode is unaffected because it
never touches the per-draft scratch or re-commits a state.

Because GDN uses head_k_dim == head_v_dim, the transposition changes no
shape and no byte count, so nothing crashes or asserts -- it silently
produces wrong tokens.

Verified on Qwen3.6-35B-A3B (Ascend 910, CANN 9.1.0-a3-B070, TP=2,
--speculative-algorithm NEXTN, gamma=4):

  before: "The  szo periódzkod ... " (word salad), accept rate 0.01-0.07
  after : "The capital of France is **Paris**." , accept rate 0.14-0.66

KDA keeps the [.., HV, V, K] layout introduced together with it.
gjsheu added a commit to gjsheu/sglang that referenced this pull request Sep 20, 2026
…c verify

sgl-project#39589 dropped the NPU transpose in MambaPool's speculative state while
migrating the Kimi-K3 (KDA) path to the canonical contiguous
[.., HV, V, K] layout. KDA is self-consistent there -- ascend_kda_backend.py
switches its kernels to that layout and its commit kernel,
move_intermediate_cache_kda(), is stride aware -- but the GDN path was left
on the [.., HV, K, V] convention:

  * AscendGDNAttnBackend / AscendHybridLinearAttnBackend reshape the
    per-draft scratch as (-1, num_value_heads, head_k_dim, head_v_dim);
  * move_intermediate_cache() (sgl_kernel_npu), which commits the state of
    the accepted prefix back into the live SSM state after target verify,
    walks each (heads, K, V) block with a hardcoded element order and is
    not stride aware.

So the state written back after the first verify step is transposed, and
every token generated after it is garbage (word salad). Plain decode is
unaffected because it never touches the per-draft scratch nor commits a
state back.

GDN uses head_k_dim == head_v_dim, so the flip changes no shape and no byte
count: nothing crashes and no assertion fires.

Verified on Qwen3.6-35B-A3B (Ascend 910, CANN 9.1.0-a3-B070, TP=2,
--speculative-algorithm NEXTN, gamma=4):

  before: "The  szo periódzkod ... " (word salad), accept rate 0.01-0.07
  after : "The capital of France is **Paris**." , accept rate 0.14-0.66

KDA (Kimi-K3) is deliberately left on its untransposed layout; no K3
checkpoint was available on the verification machine, so that path is
guarded rather than retested.
TallMessiWu added a commit to TallMessiWu/sglang that referenced this pull request Sep 20, 2026
Drop the graph-rebind fix: upstream sgl-project#39589 fixes the same race, and more
completely. Both versions order the input rebind before the replay -- ours by
doing it on the calling thread, upstream's by blocking on the future -- but
upstream reuses one device-bound worker whose executor initializer calls
set_device, instead of creating a thread per replay. Take upstream's file
whole; nothing of ours is left to carry.

The other ten files merged without conflicts.
yuychang added a commit to yuychang/sglang that referenced this pull request Sep 30, 2026
- Upstream sgl-project#39589 narrowed do_fuse_qkvbfg to quant_config is None and attn_tp == tp
- The branch was built when it was attn_tp == tp and (quant_config is None or use_full_rank_gate)
- Under the new definition Quark K3 checkpoints turn off the in-proj merge, group-64 and PTPC merge gates
- Gate those three ROCm-only paths on a new _attn_tp_is_full_tp (attn_tp == tp) to restore the validated behavior
- The loader and the low-rank fused path keep upstream's do_fuse_qkvbfg unchanged
yuychang added a commit to yuychang/sglang that referenced this pull request Oct 1, 2026
- Upstream sgl-project#39589 narrowed do_fuse_qkvbfg to quant_config is None and attn_tp == tp
- The branch was built when it was attn_tp == tp and (quant_config is None or use_full_rank_gate)
- Under the new definition Quark K3 checkpoints turn off the in-proj merge, group-64 and PTPC merge gates
- Gate those three ROCm-only paths on a new _attn_tp_is_full_tp (attn_tp == tp) to restore the validated behavior
- The loader and the low-rank fused path keep upstream's do_fuse_qkvbfg unchanged
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

jit-kernel memory-pool npu run-ci CI: run the baseline test suite on this PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants