Skip to content

feat(kda): integrate FlashInfer prefill checkpoints - #41400

Merged
kpham-sgl merged 22 commits into
sgl-project:mainfrom
kpham-sgl:kpham/flashinfer-kda-prefill
Oct 9, 2026
Merged

kpham-sgl merged 22 commits into
sgl-project:mainfrom
kpham-sgl:kpham/flashinfer-kda-prefill

Conversation

@kpham-sgl

@kpham-sgl kpham-sgl commented Sep 27, 2026 •

Copy link
Copy Markdown
Collaborator

Motivation

FlashInfer #5021 (0.7.0) adds indexed, floor-aligned checkpoints to CuTe DSL KDA prefill, which SGLang needs for radix prefix caching.

Modifications

  • Opt-in --linear-attn-prefill-backend flashinfer for BF16, 128-dim safe-gate KDA on SM100/SM103.
  • Map FlashInfer checkpoints to radix-cache track rows (FP32 state pools, padded gate/beta); write only radix-tracked checkpoints.
  • KDA models pass raw beta logits in every phase. Kernel adapters own activation, without backend-dependent model flags or model mutation during backend construction. FlashInfer tensor validation runs during warmup.
  • Call flashinfer.kda.recurrent_kda(..., backend="cute-dsl") directly. FlashInfer owns scheduling and workspace allocation; SGLang maps radix-cache checkpoint destinations once per tracked batch. Invalid scheduler/checkpoint metadata raises an assertion.
  • Reject incompatible checkpoint intervals, TBO, and full prefill CUDA graphs at setup. FlashInfer validates safe gates during warmup. BCG keeps linear attention eager; verify uses its separately configured backend.
  • Retain Triton fallback for a packed prefill containing only one token, which FlashInfer's current packed-prefill API excludes.

This PR leaves dependency pins unchanged; the current tree already pins FlashInfer 0.7.0.post1.

Accuracy Tests

Historical default-selection experiment (reverted by c0d8467478; FlashInfer remains opt-in): pre-commit passed. The registered CPU rerun passed all 10 tests on a231d574ae, after correcting the policy fixture to publish a resolved CUDA graph config. The B200 rerun passed FlashInfer KDA kernel tests and GLM-5.3-Flash low-latency GSM8K on 70b968ef19: score 0.932 over 500 questions. The server log confirms decode=triton, prefill=flashinfer, verify=nv_cutedsl. The follow-up commit changes only the unit fixture.

Previously reported on 8xGB300, FlashInfer 0.7.0: the original five KDA prefill cases, existing KDA decode/verify (11), and CuTe DSL KDA + GDN checkpoint smoke (2) passed.

The current GPU test file contains six checkpoint/layout cases and two numerical backend cases (tracked prefill and single-token fallback). The direct-call cleanup passes pre-commit and focused CPU checks: eight checked-in test methods, 22 raw-beta contract checks, six checkpoint harness cases exercising the production adapter with a substituted numerical kernel, and six metadata-hook cases. GPU imports and numerical kernels were isolated for these local CPU checks. The eight GPU cases passed on pre-rebase head ae9847e073 on B300 (SM103), FlashInfer 0.7.0.post1 and CUDA 13.0, in 207.87 seconds. The exact rebased-head rerun is reported below.

Exact rebased-head GLM-5.3-Flash checks

Reran both prefix-cache methods on the exact rebased head 706048f3fd (before the default-selection change), with the full upstream fix and no diagnostic patch: real zai-org/GLM-5.3-Flash weights (revision eb9eb208eb0d988989d07a6a12d0fdeb5f52574a), 4xB300 / TP4, BF16 KV and SSM caches, TileLang DSA, Triton decode, 16K prefill chunks, eager execution, and --mamba-track-interval 256. FlashInfer 0.7.0.post1, CUDA 13.0, PyTorch 2.14.1 (Python TreeCore), Transformers 5.17.0. Both arms use SGLANG_JIT_DEEPGEMM_PRECOMPILE=0 to compile the exercised GEMM shapes on demand; the initial exhaustive startup compilation was stopped before probe requests.

Kernel/configuration checks: 23 tests passed, including all eight FlashInfer KDA numerical/layout cases, in 229.46 seconds. Pre-commit and focused CPU contract/metadata checks also passed.

Cold versus cached continuation: score the same fixed 167-token continuation after 32,769 / 36K / 72K context tokens. All target scores are finite; inputs are identical between backends.

KDA prefill Context Cold/cached absolute logprob difference mean / p90 / max Warm/repeat mean Cached tokens warm / repeat
triton 32769 0.022187 / 0.000521 / 2.914499 0.017015 32768 / 32768
triton 36000 0.006804 / 0.000954 / 0.562500 0.003408 35840 / 35840
triton 72000 0.005175 / 0.000746 / 0.500000 0.003549 71936 / 71936
flashinfer 32769 0.009283 / 0.000480 / 1.046875 0.003289 32768 / 32768
flashinfer 36000 0.003019 / 0.000722 / 0.312500 0.003292 35840 / 35840
flashinfer 72000 0.003710 / 0.000662 / 0.346874 0.002871 71936 / 71936

Branching corruption check from #42178: cache an 8,320-token prefix; score copying a 167-token span from branch A before and after an unrelated branch B extends the shared prefix. Three different spans/seeds per backend.

KDA prefill Copy mean logprob before After competing request Second request cache hit
triton -0.015645 -0.016829 8192
flashinfer -0.015597 -0.016357 8192

The pre-rebase controls dropped from approximately -0.02 to -1.35 after the competing request. The rebased runs retain high copy scores with both KDA prefill backends and resume at 8,192, keeping the pooled index group private. This reproducer no longer shows the large branching collapse.

Warm repeats and the aligned-boundary control are not bit-exact on GLM. The Triton aligned control has a 2.9145 maximum cold/cached difference and a 2.0505 maximum warm/repeat difference despite a small p90; these outliers preclude claiming exact cache parity. These measurements do not isolate the residual drift to a specific operator. HiCache host restore and current-head speed were not tested.

GLM-5.3-Flash checks before rebase

2026-10-07, head ae9847e073, real GLM-5.3-Flash weights, 4xB300 TP4, BF16 KV/SSM caches, TileLang DSA, 16K prefill chunks, eager execution, Triton decode. FlashInfer 0.7.0.post1, CUDA 13.0, PyTorch 2.14.1 (Python TreeCore), Transformers 5.17.0. Model revision eb9eb208eb0d988989d07a6a12d0fdeb5f52574a.

Cold versus cached scoring uses identical contexts and a fixed 167-token continuation. Values are absolute logprob difference mean / p90 / max.

Layout / prefill 36K 72K
PR head, Triton 0.0044 / 0.0006 / 0.312 0.0023 / 0.0004 / 0.178
PR head, FlashInfer 0.0081 / 0.0009 / 0.550 0.0043 / 0.0007 / 0.344
Device fix, Triton 0.0089 / 0.0004 / 0.595 0.0040 / 0.0008 / 0.314
Device fix, FlashInfer 0.0045 / 0.0007 / 0.312 0.0054 / 0.0005 / 0.500

The branching check warms a prefix ending inside a 256-token index group, scores copying a span from the first suffix, then rescoring follows a competing request with unrelated text. Values below average three different 167-token copy spans.

Layout / prefill Copy mean logprob before After competing request Second request cache hit
PR head, Triton -0.0189 -1.3455 8320
PR head, FlashInfer -0.0169 -1.3452 8320
Device fix, Triton -0.0158 -0.0147 8192
Device fix, FlashInfer -0.0145 -0.0159 8192

Before rebase, head ae9847e073 lacked #42178. Both backends reproduced its pooled-index branching corruption; the device-cache portion of that fix, ported only into the isolated test checkout, removed the collapse. The PR has now been rebased onto main 7760d5264a, which contains the full merged #42178 fix. The earlier diagnostic measurements below retain their original source provenance.

Warm repeats and the 32,768-token aligned-resume control are not bit-exact on GLM; residual small drift is not isolated to KDA. At 72K on the old layout, warm/repeat cache depths also differ (65,536 / 71,936). Fixed-layout arms use the required 256-token decode tracking interval; old-layout arms use 64. Probe requests generate zero tokens, and prefill tracking follows page/chunk geometry. HiCache host restore and latest-head speed were not tested.

Historical Kimi K3 measurements

The following Kimi K3 results predate the current direct-call cleanup.

Kimi K3 real weights, 8xGB300, BF16 KV, DSpark: cold vs prefix-cache-resume logprobs over a fixed 167-token continuation (|Δlogprob| mean / p90 / max). Warm vs warm is bit-exact in all cases.

Config 36k ctx 72k ctx
DCP8, Triton prefill 0.044 / 0.149 / 0.69 0.050 / 0.174 / 1.04
DCP8, FlashInfer prefill 0.043 / 0.147 / 0.55 0.055 / 0.215 / 0.61
TP8, Triton prefill 0.051 / 0.190 / 0.69 0.059 / 0.213 / 0.86

FlashInfer matches Triton. The drift is from chunk layout, not state restore: a resume on a cold chunk boundary (32768) is bit-exact.

Speed Tests

These are historical measurements from the wrapper-based integration. The current direct API can read packed sequence offsets on the host to size its workspace and allocates that workspace within each call; performance and D2H behavior need remeasurement on the latest head.

Kimi K3 NVFP4 dummy weights, 8xGB300 TP8/DCP8, bs1, 131k in / 1k out, 95% prefix hit, A/B/A x10:

Metric Triton FlashInfer
Normal TTFT, median 0.429 s 0.411 s
Output throughput, median 406.5 tok/s 407.4 tok/s
KDA GPU time per layer (EXTEND trace) 421 µs 219 µs
KDA-associated D2H copies 0 0

An intermittent ~6.7 s TTFT stall appears with both backends and is not attributed to KDA.

Kernel-only KDA prefill (checkpoints off), K3 shape on one TP8 rank (12 heads x 128), single GB300, bf16 in / fp32 state, median of 5 (ms). Backends that fall back to Triton at these lengths are omitted: nvidia_kda (needs >1 sequence), flashkda (caps at 2048 tokens/seq).

Case Triton FlashInfer ptx_kda cutedsl
16k 1.18 0.87 0.72 1.51
32k 2.09 1.37 1.13 2.67
64k 3.95 2.38 1.94 4.94
128k, one sequence 7.76 4.36 3.65 9.61
128k as 8x16k chunks 3.82 1.18 2.66 7.52

FlashInfer is 1.3-1.8x Triton on a single sequence and 3.2x on the 8x16k shape chunked prefill actually issues, where it batches the sequences into one kernel. ptx_kda is ~20% faster on a single sequence but 2.3x slower on that chunked shape; it is also not measured here with checkpoints on, which its own fallback note says routes tracked batches to Triton. cutedsl cannot serve K3 at all (no safe gate, no interior checkpoints).

Checklist

  • Format and code style checks
  • Add registered tests
  • Kernel-level accuracy and speed results
  • Full Kimi K3 output parity with real weights

CI States

Latest PR Test (Base): ⏳ Run #37809707868
Latest PR Test (Extra): ❌ Run #37850689948
Latest PR Test (AMD ROCm 10): ⏳ Run #37809708463

@github-actions github-actions Bot added documentation Improvements or additions to documentation dependencies Pull requests that update a dependency file labels Sep 27, 2026
@kpham-sgl
kpham-sgl force-pushed the kpham/flashinfer-kda-prefill branch from fe4cfba to 0c7733a Compare September 27, 2026 07:34
@kpham-sgl kpham-sgl changed the title feat(kda): integrate FlashInfer 0.7 prefill checkpoints feat(kda): integrate FlashInfer prefill checkpoints Sep 27, 2026
@kpham-sgl kpham-sgl removed the dependencies Pull requests that update a dependency file label Sep 27, 2026
@kpham-sgl
kpham-sgl marked this pull request as ready for review September 30, 2026 18:38
@kpham-sgl
kpham-sgl force-pushed the kpham/flashinfer-kda-prefill branch from ba17536 to 30ec510 Compare September 30, 2026 20:52
@github-actions github-actions Bot added the npu label Oct 7, 2026
@kpham-sgl
kpham-sgl force-pushed the kpham/flashinfer-kda-prefill branch from ae9847e to 706048f Compare October 8, 2026 00:18
@kpham-sgl

Copy link
Copy Markdown
Collaborator Author

Rebased and force-pushed this PR onto main 7760d5264a, which includes #42178. New head: 706048f3fd. All 19 PR commits replayed without conflicts; range-diff shows only upstream context changes.

Reran both prefix-cache methods on the exact rebased head, with the full upstream fix and no diagnostic patch: real zai-org/GLM-5.3-Flash weights (revision eb9eb208eb0d988989d07a6a12d0fdeb5f52574a), 4xB300 / TP4, BF16 KV and SSM caches, TileLang DSA, Triton decode, 16K prefill chunks, eager execution, and --mamba-track-interval 256. FlashInfer 0.7.0.post1, CUDA 13.0, PyTorch 2.14.1 (Python TreeCore), Transformers 5.17.0. Both arms use SGLANG_JIT_DEEPGEMM_PRECOMPILE=0 to compile the exercised GEMM shapes on demand; the initial exhaustive startup compilation was stopped before probe requests.

Kernel/configuration checks: 23 tests passed, including all eight FlashInfer KDA numerical/layout cases, in 229.46 seconds. Pre-commit and focused CPU contract/metadata checks also passed.

Cold versus cached continuation: score the same fixed 167-token continuation after 32,769 / 36K / 72K context tokens. All target scores are finite; inputs are identical between backends.

KDA prefill Context Cold/cached absolute logprob difference mean / p90 / max Warm/repeat mean Cached tokens warm / repeat
triton 32769 0.022187 / 0.000521 / 2.914499 0.017015 32768 / 32768
triton 36000 0.006804 / 0.000954 / 0.562500 0.003408 35840 / 35840
triton 72000 0.005175 / 0.000746 / 0.500000 0.003549 71936 / 71936
flashinfer 32769 0.009283 / 0.000480 / 1.046875 0.003289 32768 / 32768
flashinfer 36000 0.003019 / 0.000722 / 0.312500 0.003292 35840 / 35840
flashinfer 72000 0.003710 / 0.000662 / 0.346874 0.002871 71936 / 71936

Branching corruption check from #42178: cache an 8,320-token prefix; score copying a 167-token span from branch A before and after an unrelated branch B extends the shared prefix. Three different spans/seeds per backend.

KDA prefill Copy mean logprob before After competing request Second request cache hit
triton -0.015645 -0.016829 8192
flashinfer -0.015597 -0.016357 8192

The pre-rebase controls dropped from approximately -0.02 to -1.35 after the competing request. The rebased runs retain high copy scores with both KDA prefill backends and resume at 8,192, keeping the pooled index group private. This reproducer no longer shows the large branching collapse.

Warm repeats and the aligned-boundary control are not bit-exact on GLM. The Triton aligned control has a 2.9145 maximum cold/cached difference and a 2.0505 maximum warm/repeat difference despite a small p90; these outliers preclude claiming exact cache parity. These measurements do not isolate the residual drift to a specific operator. HiCache host restore and current-head speed were not tested.

@kpham-sgl
kpham-sgl requested a review from Qiaolin-Yu as a code owner October 8, 2026 02:01
@kpham-sgl

Copy link
Copy Markdown
Collaborator Author

/rerun-test test/registered/unit/layers/attention/test_linear_attn_config.py test/registered/attention/test_kda_prefill_flashinfer.py test/registered/e2e/models/test_glm53_flash_b200.py::TestGLM53FlashB200LowLatency.test_gsm8k

@github-actions

github-actions Bot commented Oct 8, 2026 •

Copy link
Copy Markdown
Contributor

🚀 ubuntu-latest (1 test): ❌ View workflow run

cd test/ && python3 registered/unit/layers/attention/test_linear_attn_config.py

🚀 4-gpu-b200 (2 tests): ✅ View workflow run

cd test/ && python3 registered/attention/test_kda_prefill_flashinfer.py
cd test/ && python3 registered/e2e/models/test_glm53_flash_b200.py TestGLM53FlashB200LowLatency.test_gsm8k

@kpham-sgl

Copy link
Copy Markdown
Collaborator Author

/rerun-test test/registered/unit/layers/attention/test_linear_attn_config.py

@github-actions

github-actions Bot commented Oct 8, 2026 •

Copy link
Copy Markdown
Contributor

🚀 ubuntu-latest (1 test): ✅ View workflow run

cd test/ && python3 registered/unit/layers/attention/test_linear_attn_config.py

@kpham-sgl

kpham-sgl commented Oct 8, 2026 •

Copy link
Copy Markdown
Collaborator Author

Retested current opt-in head c0d84674789f on 4×GB300 (SM103), explicitly adding --linear-attn-prefill-backend flashinfer to the registered GLM-5.3-Flash test launches locally. Production defaults remain reverted. Test workloads and thresholds were unchanged.

Test Result
FlashInfer KDA kernel suite 8/8 passed
GLM Low Latency: single-request speed + GSM8K 2/2 passed; 465.43 token/s, acceptance 4.324; GSM8K 0.938
GLM High Throughput: GSM8K, DP4 + breakable prefill graphs Passed; GSM8K 0.944
GLM DFlash2: GSM8K Passed; GSM8K 0.938

Environment: PyTorch 2.14.1+cu130, FlashInfer 0.7.0.post1, CUDA 13.0; FP8 KV and TRTLLM DSA/MoE as in test_glm53_flash_b200.py. Target revision eb9eb208eb0d988989d07a6a12d0fdeb5f52574a, DFlash2 revision bf582e4eacc1810f76656d1811693ff6c6737d2a.

These are local GB300 runs of the B200 recipe, not B200 CI results. The runner explicitly selected Python TreeCore (SGLANG_UNIFIED_RADIX_TREE_CORE_BACKEND=python) and disabled DeepGEMM precompile (SGLANG_JIT_DEEPGEMM_PRECOMPILE=0). An initial attempt failed before test execution because workers differed in Rust toolchain-probe fallback; one selected Rust without a bundled/cached extension while builds were disabled. All results above are from the subsequent consistent Python TreeCore run.

Retested Kimi-K3 locally on 8×GB300 across two four-GPU hosts, retaining the registered B300 workloads and thresholds. Added only the explicit FlashInfer prefill selection and two-node launch settings, and selected verified cached snapshots: target 9f62e4e9fffbd0a83ddd60e1c209d828994b3569, DSPARK draft 3c5bac301d9cf392706189d82ed947feca6c2f0f. The complete snapshots were copied to each host's NVMe before successful runs. The eight-rank CUDA all-reduce preflight passed.

Kimi test Result
Balanced TP8/DCP8 + HiCache Passed; GSM8K 0.985
MegaMoE TP8/EP8/DCP8 + DSPARK + linear ReplaySSM Passed; GSM8K 0.980, average speculative acceptance 4.7792
Low Latency TP8 + DSPARK: MMMU-Pro Passed; sgl-eval score 0.855, average speculative acceptance 2.6250
Low Latency: single-request speed Failed the stock 300 token/s gate; ~231 token/s, acceptance 4.708

Both hosts use the same package versions and explicit Python TreeCore selection above. These are two-node GB300 runs of the B300 recipes, not B300 CI results. An earlier shared-storage startup failed before inference with CUDA error 719 in an expert-weight CPU-to-GPU copy. The local-NVMe retry passed both accuracy tests above; the slow shard was verified against its checkpoint SHA256 on both hosts.

Ran the existing TestKimiK3B300LowLatency.test_mmmu_pro test with sgl-eval 0.1.2, once per prefill backend on fresh servers. Retained 200 examples, 64 threads, one repeat, and the Kimi-K3 preset (thinking enabled, temperature 1.0, top-p 0.95, max output 300,000 tokens; request seed unset). Both server launches used --random-seed 1. Saved predictions verify identical question IDs/text/expected answers and generation settings; resolved server configurations differ only in linear_attn_prefill_backend.

KDA prefill MMMU-Pro score Average acceptance length Errors / truncated outputs Registered test
FlashInfer 0.855 (171/200) 2.6250 0 / 0 Passed
Triton 0.830 (166/200) 2.6100 0 / 0 Passed

Both exceed the existing quality threshold 0.75 and acceptance threshold 2.4. FlashInfer's aggregate acceptance is 0.6% higher here; this MMMU-Pro run does not reproduce the single-prompt acceptance drop. The stock preset is stochastic; these are single 200-example evaluations, not an output-parity test. Evaluation latency was 1152.72s / 1112.00s, with 1,086,318 / 1,092,392 generated tokens (FlashInfer / Triton).

The separate existing test_bs_1_speed control also missed its 300 token/s gate with Triton (~281 token/s, acceptance 5.835). FlashInfer's 4.708 versus Triton's 5.835 acceptance repeated across all three greedy attempts. Both pass that test's acceptance floor of 4.0, but the speed failures remain; the Low Latency test file is not fully green. The cause of that single-prompt acceptance difference has not been isolated.

The initial MMMU attempts failed before evaluation because sgl-eval was missing and then dataset metadata targeted the shared read-only HF cache. The final runs above use the pinned evaluator and a writable local cache. The custom acceptance probe was discarded; the reported comparison comes from the existing registered sgl-eval test.

@kpham-sgl kpham-sgl added run-ci CI: run the baseline test suite on this PR and removed run-ci CI: run the baseline test suite on this PR labels Oct 8, 2026
@kpham-sgl

Copy link
Copy Markdown
Collaborator Author

/tag-and-rerun-ci

@github-actions github-actions Bot added the run-ci CI: run the baseline test suite on this PR label Oct 8, 2026
@kpham-sgl
kpham-sgl merged commit fc3385d into sgl-project:main Oct 9, 2026
211 of 289 checks passed
@nvpohanh

Copy link
Copy Markdown
Collaborator

cc @leejnau

nvpohanh added a commit to elvischenv/sglang that referenced this pull request Oct 12, 2026
Resolve the conflict in kimi_linear.py: keep this PR's logical-mode
comment and drop the gate-activation comment that sgl-project#41400 removed on main.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation 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.

2 participants