Skip to content

[Perf][GLM-5.3-Flash] Use FlashKDA for KDA chunked prefill (1.7-3.8x faster than the Triton chunk path) - #55737

Merged
ZJY0516 merged 7 commits into
vllm-project:mainfrom
JaredforReal:perf/glm53-flash-flashkda
Sep 14, 2026
Merged

ZJY0516 merged 7 commits into
vllm-project:mainfrom
JaredforReal:perf/glm53-flash-flashkda

Conversation

@JaredforReal

@JaredforReal JaredforReal commented Sep 7, 2026

Copy link
Copy Markdown
Contributor

Purpose

One of three independent GLM-5.3-Flash perf PRs

Use FlashKDA (vllm._flashkda_C, already built and used by Kimi-K3) for GLM-5.3-Flash's KDA chunked prefill instead of the ~15-kernel Triton chunk_kda_with_fused_gate path.

FlashKDA implements the same bounded-gate KDA recurrence GLM-5.3-Flash uses (lower_bound * sigmoid(exp(A_log) * (g + dt_bias)), in-kernel q/k l2norm, raw beta logits), so it is a drop-in. On GB300 per KDA layer (H=16/rank, D=128, bench/flashkda_vs_chunk.py):

shape Triton chunk path FlashKDA
8 x 2048 tokens 183 us 49 us
4 x 8192 555 us 146 us
1 x 16384 1371 us 787 us

Selection: automatic when supported (SM90/SM10x/SM12x, bf16, head_dim 128, bounded gate); additional_config.kda_prefill_backend = triton keeps the old path (flashkda forces it). The output is written straight into the layer buffer (no merge copy) and the pre-sigmoid beta cast is not needed on this path. Spec-decode tokens themselves keep the Triton recurrent kernel (num_accepted_tokens rollback); in a step that carries both, FlashKDA runs the prefill segment into a workspace buffer and the outputs are scattered back by non_spec_token_indx / spec_token_indx (same scheme as the Kimi-K3 KDA layer; the merge writes straight into core_attn_out).

Test Plan

  • Unit / kernel tests listed under "Tests" below.
  • Serving benchmark, all builds back-to-back in one session on 4x GB300 (SM100), TP4, prefix caching disabled:
    vllm serve zai-org/GLM-5.3-Flash -tp 4 --attention-backend FLASHINFER_MLA_SPARSE \
      --max-model-len 69632 --max-num-seqs 256 --max-num-batched-tokens 16384 \
      --gpu-memory-utilization 0.9 --no-enable-prefix-caching
    vllm bench serve --backend vllm --model glm53-flash --dataset-name random --random-range-ratio 0 \
      --ignore-eos --seed 0 --random-input-len <in> --random-output-len <out> --max-concurrency <c> \
      --num-prompts <n> --num-warmups <c> --percentile-metrics ttft,tpot,itl,e2el
    0 prefix-cache hit rate in the server log was checked to be 0.0% for every run.
  • Accuracy per build: lm_eval --model local-completions --tasks gsm8k --num_fewshot 5 --gen_kwargs temperature=0 (1319 questions), prompt-logprob agreement on real 4k/12k/30k/60k prompts vs main, and a 36-needle retrieval set at 6k/16k/30k (greedy, reasoning_effort=low), repeated 3x on main / +[Perf][GLM-5.3-Flash] Decode hot-path cleanups: strided KDA recurrent inputs, NoPE MQA query without concat, no duplicate router GEMM #55736 / full stack.

Test Result

Performance of this PR alone

Measured on top of #55736 (main + #55736) vs the same + this PR, 4x GB300, TP4, --attention-backend FLASHINFER_MLA_SPARSE --max-model-len 69632 --max-num-seqs 256 --max-num-batched-tokens 16384, prefix caching disabled (hit rate 0.0% checked in the server log), vllm bench serve random dataset with warmups, back-to-back in the same session. #55736 only touches decode, so this is the effect of FlashKDA on an otherwise-main prefill path. Decode tok/s is the steady-state window value; TPOT is the per-request median.

point metric +#55736 +#55736 +#55737 delta
prefill 8x2048 mean TTFT ms 449 395 -12.2%
prefill 2x8192 mean TTFT ms 380 330 -13.2%
prefill 8x32768 mean TTFT ms 5108 4693 -8.1%
prefill 2x65536 mean TTFT ms 2661 2450 -7.9%
32k/256 c=16 decode tok/s 296 316 +6.6%
32k/256 c=16 mean TTFT ms 3835 3642 -5.0%
32k/256 c=16 median TPOT ms 40.10 37.50 -6.5%
1k/512 c=1 decode tok/s 146 147 +0.7%
1k/512 c=1 median TPOT ms 6.46 6.47 +0.2%
1k/512 c=64 decode tok/s 3090 3132 +1.4%
1k/512 c=64 median TPOT ms 19.26 19.24 -0.1%
1k/512 c=256 decode tok/s 7367 7717 +4.7%
1k/512 c=256 median TPOT ms 32.87 31.30 -4.8%

With speculative decoding (MTP k=1)

Same build, same settings plus --speculative-config.method=mtp --speculative-config.num_speculative_tokens=1, kda_prefill_backend=triton vs flashkda, back-to-back. Random-dataset acceptance is ~1.2 tokens/step, so absolute decode numbers are low; the comparison is what matters. Whole-run throughput (the steady-state window accounting is off under spec decode).

point metric MTP k=1 + Triton KDA prefill MTP k=1 + FlashKDA
prefill 8x2048 mean TTFT ms 464 423 (-8.8%)
prefill 2x8192 mean TTFT ms 390 360 (-7.7%)
32k/256 c=16 decode tok/s 260 284 (+9.5%)
32k/256 c=16 mean TTFT ms 2623 2479 (-5.5%)
32k/256 c=16 median TPOT ms 52.09 47.86 (-8.1%)
1k/512 c=64 decode tok/s 2003 2130 (+6.3%)
1k/512 c=64 median TPOT ms 30.00 27.56 (-8.1%)
1k/512 c=256 decode tok/s 3697 4116 (+11.3%)
1k/512 c=256 median TPOT ms 67.96 60.53 (-10.9%)

Accuracy (per build, same session)

Each rung of the ablation was also evaluated on its own: gsm8k (1319 questions, 5-shot, greedy, lm_eval local-completions

build gsm8k flexible-extract % gsm8k strict-match %
main 93.33 ± 0.69 93.10 ± 0.70
+#55736 +#55737 93.25 ± 0.69 93.18 ± 0.69

Duplicate-work check

gh pr list --repo vllm-project/vllm --state open --search "GLM-5.3-Flash FlashKDA" / "glm5next KDA": no open PR wires FlashKDA for GLM-5.3-Flash (#55224 is the KDA conv weight refit fix).

Tests

  • bench/kda_numerics.py: FlashKDA vs the naive recurrent reference (from tests/models/kimi_k3/test_kda.py) mean|Δ| 2e-5, max 4.8e-4 on outputs, same as the Triton chunk path vs the reference (T=48, 2 sequences, lower_bound -5). Note the Triton chunk path writes its output into v's buffer, so the comparison clones v.
  • pytest tests/models/kimi_k3/test_kda.py -k flashkda: 2 passed (extension sanity).
  • e2e: chat sanity; prompt-logprob A/B vs main within ~1-2.5x the main-vs-main noise floor with identical next tokens; gsm8k 1319 questions (5-shot, greedy): main 93.03% ± 0.70, full series 92.65% ± 0.72 (measured on the three PRs together).
  • pre-commit run ruff-check / ruff-format: passed.

Notes for review

  • FlashKDA hardcodes dense q/k/v strides, so the three .contiguous() copies of the conv-output slices remain (~115 us/layer per 16k chunk); a follow-up could run the short conv per q/k/v into separate buffers.
  • AI assistance: developed with Claude and reviewed by the submitter

Essential Elements of an Effective PR Description Checklist
  • The purpose of the PR, such as "Fix some issue (link existing issues this PR will resolve)".
  • The test plan, such as providing test command.
  • The test results, such as pasting the results comparison before and after, or e2e results
  • (Optional) The necessary documentation update, such as updating supported_models.md and examples for a new model.

FlashKDA (already built as vllm._flashkda_C for Kimi-K3) implements the
same bounded-gate KDA recurrence as chunk_kda_with_fused_gate
(lower_bound * sigmoid(exp(A_log) * (g + dt_bias)), in-kernel q/k l2norm,
raw beta logits). On GB300 it replaces ~15 Triton kernels with 4 and is
1.7-3.8x faster (T=2048x8: 183->49 us, 8192x4: 555->146 us, 16384x1:
1371->787 us per layer). Select it automatically when supported
(SM90/SM10x/SM12x, bf16, head_dim 128, bounded gate);
additional_config.kda_prefill_backend = triton keeps the old path. The
output is written straight into the layer buffer and the pre-sigmoid beta
cast is no longer needed on this path.

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: Jared Wen <jaredwen@inferact.ai>
@coderabbitai

coderabbitai Bot commented Sep 7, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Team

Run ID: c4d6ead6-c50a-4e14-95e8-6a0575b38676

📥 Commits

Reviewing files that changed from the base of the PR and between 252723c and 674dd95.

📒 Files selected for processing (1)
  • vllm/models/glm5next/nvidia/kda.py

Included review availability: Your plan provides up to 10 included reviews per hour; 9 remain after this review.


📝 Summary

Summary by CodeRabbit

  • Performance

    • Improved FlashKDA processing for prefill steps that include speculative tokens.
    • Writes intermediate results directly to the output buffer, reducing temporary allocations and unnecessary data movement.
    • Improved handling of mixed prefill and speculative token workloads.
  • Compatibility

    • Preserved existing output behavior while supporting combined prefill and speculative execution paths.

Walkthrough

The KDA layer validates backend constraints and prepares FlashKDA resources. FlashKDA prefill now supports workspace-backed output for mixed speculative steps and returns both output and final state. Results are written directly into core_attn_out.

Changes

GLM-5.3-Flash KDA prefill

Layer / File(s) Summary
Prefill backend selection
vllm/models/glm5next/nvidia/kda.py
Validates auto, triton, and flashkda using CUDA capability, dtype, head dimension, and bounded-gate constraints.
FlashKDA resource preparation
vllm/models/glm5next/nvidia/kda.py
Imports the workspace manager, initializes FlashKDA state, and defines workspace output-buffer requirements for mixed prefill steps.
Prefill output routing
vllm/models/glm5next/nvidia/kda.py
Adds a prefill helper that returns output and final state. Ordinary steps write directly to the layer output. Mixed steps use workspace output and scatter speculative and non-speculative results directly into core_attn_out.

Estimated code review effort: 4 (Complex) | ~45 minutes

Merge Risk: ⚪ Minimal · up to 674dd

This change enables FlashKDA prefill on supported hardware and routes mixed speculative outputs directly into layer buffers. No concrete merge-blocking risk remains.

Sequence Diagram(s)

sequenceDiagram
  participant GLM5NextKDA
  participant FlashKDA
  participant WorkspaceManager
  participant core_attn_out
  GLM5NextKDA->>FlashKDA: execute prefill with optional output
  FlashKDA->>WorkspaceManager: obtain output and final-state buffers
  WorkspaceManager-->>FlashKDA: return workspace buffers
  FlashKDA-->>GLM5NextKDA: return output and final state
  GLM5NextKDA->>core_attn_out: write ordinary or scattered speculative results
Loading

Suggested reviewers: zjy0516

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 6 functions across 1 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly identifies the main change: using FlashKDA for GLM-5.3-Flash KDA chunked prefill. The performance context is relevant and specific.
Description check ✅ Passed The description directly explains the FlashKDA integration, backend selection, speculative-decoding support, performance results, accuracy checks, and test plan.
  • Fix all pre-merge checks with AI

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@mergify mergify Bot added the glm label Sep 7, 2026
@JaredforReal
JaredforReal marked this pull request as ready for review September 7, 2026 13:11
Copilot AI lite review requested due to automatic review settings September 7, 2026 13:11

@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.

@ZJY0516 ZJY0516 added the ready ONLY add when PR is ready to merge/full CI is needed label Sep 7, 2026
@github-actions

github-actions Bot commented Sep 7, 2026

Copy link
Copy Markdown

@JaredforReal, CI is now available for this PR.

  • /ci run starts upstream CI; /amd-ci run starts AMD CI only.
  • /ci retry retries failed jobs in the CI build for the current PR head. If the current head has no CI build, it starts a new CI build for the current head containing only jobs that failed in the latest earlier CI build for this PR.
  • /amd-ci retry retries failed jobs in AMD CI for the current PR head. Use /amd-ci run when the current head has no AMD CI build.
  • /ci cancel cancels scheduled or running CI builds for this PR branch; /amd-ci cancel does the same for AMD CI only.

Comment thread vllm/models/glm5next/nvidia/kda.py Outdated
safe_gate=safe_gate,
lower_bound=lower_bound,
)
if self.kda_prefill_backend == "flashkda" and not use_spec:

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I remember flashkda also supports spec decoding?

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.

I remember not,need more profiling and optimization on SpecDecode path tho..

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

you can check this: vllm/models/kimi_k3/nvidia/kda.py, no such guard

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.

thanks, will work on it

Copilot AI 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.

🔵 Needs a closer look

It changes a performance-critical prefill kernel path to call into a fused CUDA extension with new runtime selection and workspace usage, which warrants final human review for correctness/compatibility across execution modes.

Pull request overview

This PR wires the existing FlashKDA CUDA extension (vllm._flashkda_C) into the GLM-5.3-Flash KDA chunked prefill path, replacing the current Triton chunk_kda_with_fused_gate route when the platform/config is compatible (CUDA SM90/SM10x/SM12x, bf16, head_dim=128, bounded gate). The goal is to reduce prefill latency by using a more fused implementation and avoiding the pre-sigmoid beta casting and the merge copy on the non-spec prefill path.

Changes:

  • Add backend resolution (auto / triton / flashkda) for KDA prefill and store the chosen backend on the layer.
  • Add a FlashKDA prefill wrapper that uses the v1 workspace manager to allocate per-call scratch/state buffers and invokes torch.ops._flashkda_C.fwd.
  • Route non-spec prefill through FlashKDA when enabled; otherwise keep the existing Triton chunk path (and always keep Triton for spec-decode steps).
File summaries
File Description
vllm/models/glm5next/nvidia/kda.py Adds FlashKDA backend selection and a FlashKDA-based chunked prefill execution path, falling back to the existing Triton chunk implementation when unsupported or disallowed.
Review details
  • Files reviewed: 1/1 changed files
  • Comments generated: 0
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

@JaredforReal

Copy link
Copy Markdown
Contributor Author

/ci run

@github-actions

github-actions Bot commented Sep 7, 2026

Copy link
Copy Markdown

✅ Triggered Buildkite CI #87558 for commit 252723cd074d.

…code steps too

The FlashKDA path was gated on `not use_spec` so it could write straight
into the layer output buffer; a step that also carried spec-decode tokens
fell back to the Triton chunk path for its whole prefill segment, which with
MTP enabled is almost every step that has a prefill. Follow the Kimi-K3 KDA
layer instead: in a spec step FlashKDA writes to a workspace buffer and the
non-spec tokens are scattered by non_spec_token_indx, alongside the spec
tokens (which keep the recurrent kernel with num_accepted_tokens rollback).
The merge now index_copy_s directly into core_attn_out instead of going
through a temporary tensor.

MTP k=1, TP4 on 4x GB300, same build, Triton -> FlashKDA: 8x2048 TTFT
464 -> 423 ms, 2x8192 390 -> 360 ms, 32k/256 c=16 260 -> 284 tok/s,
1k/512 c=64 2003 -> 2130 tok/s; gsm8k 93.33 vs 93.56 (+-0.7).

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: Jared Wen <jaredwen@inferact.ai>
@JaredforReal

Copy link
Copy Markdown
Contributor Author

/ci run

@github-actions

github-actions Bot commented Sep 9, 2026

Copy link
Copy Markdown

✅ Triggered Buildkite CI #87906 for commit 8050130234a5.

@NNNtrance

Copy link
Copy Markdown

Field report from a different stack, in case it helps the review: three DGX Spark GB10 nodes (sm_121, arm64, CUDA 13.0) serving GLM-5.3-Flash as an EXL3 4.05 bpw checkpoint at TP=3 + expert parallelism, vLLM pinned at 487ecf1 with this PR's kda.py change ported as a startup patch. The _flashkda_C in our image is the 26 Aug build; its family-120 cubin runs on sm_121 without an sm_121 target.

  • Kernel microbench at production shape (22 KDA heads per rank, head_dim 128, bf16, bounded gate), 2048 tokens: 2.47 ms on the Triton chunk path → 0.97 ms with FlashKDA, the three .contiguous() copies included (2.55×). Numerics against the Triton path: mean |Δ| 1.2e-5, max 9.8e-4 on the outputs, i.e. bf16-ULP scale.
  • End to end, symmetric A/B (both arms booted the same way, only the backend differs; 24 fresh ~7,000-token prompts kept in flight, 60 s windows, zero prefix-cache hits): prefill 1,753.5 / 1,754.0 → 1,867.4 / 1,868.8 tok/s (+6.5 %), TTFT for a 7K prompt 4.19 → 3.98 s (−5.1 %), single-stream decode unchanged (61.2 vs 61.3 tok/s), KV pool unchanged, quality gates unchanged (correctness probe 10/10, code exam 12/12, 6-depth needle 6/6, vision OK).
  • Our prefill profile puts the replaced kernels at about 6 % of GPU time, so we expected +3.5–4 %; the measured +6.5 % is more than that share explains and we have not root-caused the difference.
  • One small suggestion: log which backend was resolved. We added a one-line kda_prefill_backend=flashkda|triton at startup; without it an A/B cannot be verified from the logs.

Full write-up, harness and the port: https://github.com/NNNtrance/GLM-5.3-Flash-EXL3-DGX-Spark/blob/main/results/gates/flashkda-ab-10sep.md. It is in our production as of today. Thanks for the port.

@ZJY0516

ZJY0516 commented Sep 10, 2026

Copy link
Copy Markdown
Member

/ci run

@github-actions

Copy link
Copy Markdown

✅ Triggered Buildkite CI #88089 for commit 42a36a6e3cbf.

@gaby

gaby commented Sep 11, 2026

Copy link
Copy Markdown

@JaredforReal I noticed you are using --attention-backend FLASHINFER_MLA_SPARSE but none of the official recipes do. Is that better for performance?

@JaredforReal

Copy link
Copy Markdown
Contributor Author

@gaby nope, now glm-5.3-flash in vllm has only one attention backend, which is FLASHINFER_MLA_SPARSE
Adding the flag is only for clarity

@JaredforReal

Copy link
Copy Markdown
Contributor Author

/ci run

@github-actions

Copy link
Copy Markdown

✅ Triggered Buildkite CI #88550 for commit 4792ef280b9d.

@ZJY0516

ZJY0516 commented Sep 14, 2026

Copy link
Copy Markdown
Member

/ci run

@ZJY0516
ZJY0516 enabled auto-merge (squash) September 14, 2026 07:11
@github-actions

Copy link
Copy Markdown

✅ Triggered Buildkite CI #88754 for commit 360165d53160.

@ZJY0516
ZJY0516 merged commit b443c1c into vllm-project:main Sep 14, 2026
72 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

glm ready ONLY add when PR is ready to merge/full CI is needed

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants