Conversation
📝 WalkthroughWalkthroughAdds a packed Kimi K3 T=1 recurrent decode path. The change includes exact-target CUDA kernels, JIT/AOT integration, public API and trace support, Blackwell correctness tests, and a strict CUPTI benchmark with JSON provenance reporting. ChangesPacked KDA decode
Estimated code review effort: 5 (Critical) | ~120 minutes Sequence Diagram(s)sequenceDiagram
participant Caller
participant packed_kda_decode
participant run_packed_kda_decode
participant JITModule
participant CUDAKernel
participant StatePool
Caller->>packed_kda_decode: pass packed QKV, gate, beta, and state
packed_kda_decode->>run_packed_kda_decode: forward decode request
run_packed_kda_decode->>JITModule: select target and batch variant
JITModule->>CUDAKernel: launch on current CUDA stream
CUDAKernel->>StatePool: read and update recurrent state
CUDAKernel-->>run_packed_kda_decode: produce output tensor
run_packed_kda_decode-->>Caller: return output
Possibly related PRs
Suggested labels: Suggested reviewers: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches 💡 1🛠️ Fix failing CI checks 💡
🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (4)
benchmarks/bench_packed_kda_decode.py (2)
339-350: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winReplace the assigned lambdas with local functions.
Ruff reports E731 for Lines 341 and 342.
♻️ Proposed fix
if mode == "direct": - warmup_run = lambda: _run(warmup_case) - measured_run = lambda: _run(case) + + def warmup_run(): + return _run(warmup_case) + + def measured_run(): + return _run(case) + elif mode == "cuda_graph":🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@benchmarks/bench_packed_kda_decode.py` around lines 339 - 350, Replace the lambda assignments in _make_timing_runners for direct mode with local def functions that invoke _run on warmup_case and case, preserving the returned warmup_run and measured_run behavior.Source: Linters/SAST tools
353-380: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winDocument the
6warmup-call budget used by CUPTI timing.
warmup_calls = 6 + warmup_itersencodes the extrastaged_runinvocation made whilebench_gpu_timeestimates execution time: one initial overhead-exclusion call plus five estimation iterations.cold_l2_cache=Truedoes not add extra runner calls here, butuse_cuda_graph=Truewould change this path. Add an inline comment stating which internal calls this budget covers so helper changes catch the issue early and the benchmark does not stop on a stale constant.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@benchmarks/bench_packed_kda_decode.py` around lines 353 - 380, Document the warmup_calls calculation in _timed_sample with an inline comment explaining that the fixed 6 covers one overhead-exclusion invocation plus five execution-time estimation iterations, while warmup_iters accounts for dry-run calls. Note that cold_l2_cache does not add runner calls here and this budget assumes use_cuda_graph is disabled.tests/kda/test_packed_kda_decode.py (1)
45-54: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winAdd a CUDA-version guard to the skip condition.
The fixture only checks the compute capability.
flashinfer/kda_kernels/packed_kda_decode.pyalso requires CUDA 12.8 forsm100aand CUDA 12.9 forsm103a, as the parametrized cases in Lines 654-662 show. On a CC 10.0 or 10.3 device with an older CUDA toolkit,_target_for_deviceraisesRuntimeErrorand every GPU test fails instead of skipping.Add the version check to the fixture so the tests skip on unsupported toolkits.
♻️ Proposed guard
device = torch.device("cuda") - if torch.cuda.get_device_capability(device) not in ((10, 0), (10, 3)): + capability = torch.cuda.get_device_capability(device) + if capability not in ((10, 0), (10, 3)): pytest.skip( "packed KDA T=1 requires exact CC 10.0 (SM100a) or CC 10.3 (SM103a)" ) + required_cuda = "12.8" if capability == (10, 0) else "12.9" + if not is_cuda_version_at_least(required_cuda): + pytest.skip(f"packed KDA T=1 on CC {capability} requires CUDA {required_cuda}") return deviceImport the helper from
flashinfer.utils.As per coding guidelines: "architecture-specific tests must skip unsupported GPUs using the appropriate
flashinfer.utilschecks or backend capability APIs".🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@tests/kda/test_packed_kda_decode.py` around lines 45 - 54, Update the packed_kda_device fixture to also validate the installed CUDA version using the appropriate helper from flashinfer.utils, alongside the existing torch.cuda availability and compute-capability checks. Skip CC 10.0 devices unless CUDA 12.8 is supported and CC 10.3 devices unless CUDA 12.9 is supported, preserving the current behavior for supported configurations.Source: Coding guidelines
flashinfer/jit/flash_kda_packed_t1.py (1)
75-80: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueDerive the selector threshold from the variant metadata.
FLASH_KDA_PACKED_T1_VARIANT_METADATAdeclaresbatch_minandbatch_max, but_variant_for_batchhard-codes32. The threshold now lives in two places. A future retune must change both, and nothing in the module enforces that they agree.♻️ Proposed refactor
def _variant_for_batch(batch: int) -> FlashKDAPackedT1Variant: """Select the frozen schedule using only host-visible shape metadata.""" if batch <= 0: raise ValueError(f"packed KDA T=1 batch must be positive, got {batch}") - return "tile16" if batch >= 32 else "tile8" + for variant in FLASH_KDA_PACKED_T1_VARIANTS: + metadata = FLASH_KDA_PACKED_T1_VARIANT_METADATA[variant] + if batch >= metadata.batch_min and ( + metadata.batch_max is None or batch <= metadata.batch_max + ): + return variant + raise ValueError(f"no packed KDA T=1 schedule covers batch {batch}")🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@flashinfer/jit/flash_kda_packed_t1.py` around lines 75 - 80, Update _variant_for_batch to derive the batch-selection threshold from FLASH_KDA_PACKED_T1_VARIANT_METADATA rather than hard-coding 32, using the declared variant batch bounds to preserve tile8 below the boundary and tile16 at or above it.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@benchmarks/bench_packed_kda_decode.py`:
- Line 56: Validate TYPED_SOURCE_SHA256 against the SHA-256 digest of
csrc/kda/flashkda_packed_t1_binding.cuh from disk before publishing the
benchmark report. Extend the existing verification flow around
_verify_frozen_body_hashes() to raise on mismatch, ensuring report generation
cannot proceed with an incorrect typed-source hash.
In `@csrc/kda/flashkda_packed_t1_binding.cuh`:
- Around line 32-55: Pre-include <math_constants.h> alongside the existing CUDA
headers, before the fixed-width type and tensor-map rename macros in the
binding. Keep the rename region and generated-body inclusion unchanged so the
frozen body’s guarded nested include becomes a no-op.
In `@flashinfer/jit/flash_kda_packed_t1.py`:
- Around line 236-246: Reorder the entries in __all__ so
FLASH_KDA_PACKED_T1_VARIANTS appears before
FLASH_KDA_PACKED_T1_VARIANT_METADATA, satisfying RUF022 while leaving all
exported symbols unchanged.
---
Nitpick comments:
In `@benchmarks/bench_packed_kda_decode.py`:
- Around line 339-350: Replace the lambda assignments in _make_timing_runners
for direct mode with local def functions that invoke _run on warmup_case and
case, preserving the returned warmup_run and measured_run behavior.
- Around line 353-380: Document the warmup_calls calculation in _timed_sample
with an inline comment explaining that the fixed 6 covers one overhead-exclusion
invocation plus five execution-time estimation iterations, while warmup_iters
accounts for dry-run calls. Note that cold_l2_cache does not add runner calls
here and this budget assumes use_cuda_graph is disabled.
In `@flashinfer/jit/flash_kda_packed_t1.py`:
- Around line 75-80: Update _variant_for_batch to derive the batch-selection
threshold from FLASH_KDA_PACKED_T1_VARIANT_METADATA rather than hard-coding 32,
using the declared variant batch bounds to preserve tile8 below the boundary and
tile16 at or above it.
In `@tests/kda/test_packed_kda_decode.py`:
- Around line 45-54: Update the packed_kda_device fixture to also validate the
installed CUDA version using the appropriate helper from flashinfer.utils,
alongside the existing torch.cuda availability and compute-capability checks.
Skip CC 10.0 devices unless CUDA 12.8 is supported and CC 10.3 devices unless
CUDA 12.9 is supported, preserving the current behavior for supported
configurations.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: ae6f3a64-588f-45e9-b0d1-34106e163c53
📒 Files selected for processing (15)
benchmarks/bench_packed_kda_decode.pycsrc/kda/flashkda_packed_t1_binding.cuhcsrc/kda/flashkda_packed_t1_tile16.cucsrc/kda/flashkda_packed_t1_tile8.cudocs/api/kda_decode.rstflashinfer/__init__.pyflashinfer/aot.pyflashinfer/jit/flash_kda_packed_t1.pyflashinfer/kda_decode.pyflashinfer/kda_kernels/__init__.pyflashinfer/kda_kernels/packed_kda_decode.pyflashinfer/trace/templates/kda.pytests/jit/test_flash_kda_packed_t1_jit.pytests/kda/test_packed_kda_decode.pytests/trace/example.py
|
#4417 I have this PR which kda decode kernel based on my kernel gdn decode kernel which is ~10.7% better than this version.
Methodology:
|
|
Superseded by #4445. That PR contains this draft's serving-native packed T=1 decode API and frozen SM100a/SM103a kernels, plus the strided-prefill, indexed-state, native-checkpoint, and later build-boundary hardening work. Closing this draft to keep review on the consolidated PR; this branch and its review history remain available. |
…de (#4445) Related to #4254. Supersedes #4378. Adds native Blackwell recurrent-KDA prefill and packed T=1 decode support, including the serving contracts used by [sgl-project/sglang#34299](sgl-project/sglang#34299). The public backend name remains `cake_kda`. ## API behavior - `flashinfer.recurrent_kda` automatically dispatches eligible BF16 fixed or packed-varlen prefill on SM100a/SM103a to the frozen FlashKDA kernels while preserving existing decode behavior. - `beta` may be a positive, non-overlapping token-row-strided view with unit head stride. Indexed recurrent-state pools, padded physical slot strides, native intermediate checkpoints, caller-owned output, exact state aliasing, current-stream execution, and CUDA Graph workspace reuse are supported. - `flashinfer.kda_decode.recurrent_kda(..., backend="cake")` explicitly selects the exported Cake decode backend. `packed_kda_decode` consumes serving-native packed Kimi-K3 tensors and caller-owned indexed state. ## Export and routing This PR is the complete export of Cake runtime `e1782b2feb65590d20ec0c67e8d42ac3ec18321a`. The frozen device sources are from Cake `f4b7f427af886312529c718bc4c8de1a2848ad56`; the functional changes are host/binding routing repairs, so the exported CUDA is unchanged. Current HEAD `83ed637db8912856f44900b938df1e2e335d2002` contains the review follow-ups: CUDA 12.9+ uses one `sm100f` family cubin for CC10.0 and CC10.3, CUDA 12.8 CC10.0 retains `sm100a`, persistent routing remains limited to physical CC10.0 with a measured 148/152-SM count, shared tensor-check utilities replace local duplicates, the benchmark summarizer has its clearer name, and the synthetic benchmark-report test is removed. Public generated identifiers and comments use standalone FlashKDA terminology. No SHA-256 identity gate or test is part of the export. The 29-shape matrix below is a validation denominator, not a runtime allowlist; other calls satisfying the documented frozen prefill contract also route to Cake, while ordinary ineligible calls retain the existing backend fallback. The runtime caches `torch.cuda.get_device_properties(device).multi_processor_count` separately from the ISA target and passes it to route selection, persistent worker count, and LPT bin construction: - B200 `(sm_100a, 148 SM)` and GB200 `(sm_100a, 152 SM)` use persistent M128 for measured H96 mixed/uniform and H64 uniform packed shapes. - B300 `(sm_103a, 148 SM)` and GB300 `(sm_103a, 152 SM)` use direct M128. - Compatible fixed H64 uses M64; H12 uses direct N16. Strided beta, indexed state, and checkpoints remain on direct routes. - The H96 uniform N128 holdout uses exact direct N16 on the two 148-SM devices, persistent M128 on GB200, and direct M128 on GB300. ## Final four-SKU validation Final head `83ed637db8912856f44900b938df1e2e335d2002` passed on B200, GB200, B300, and GB300 with CUDA 13.3. Each device passed 43/43 JIT/import/AOT contracts and 83/83 GPU/API/stream/graph tests; all six recurrent-prefill and packed-decode modules built and loaded from `compute_100f,sm_100f` cubins. The public dispatcher used persistent routes only on CC10.0 and direct routes on CC10.3, independently of the physical 148/152-SM count. The family-target decision used same-device, order-balanced, cold-L2 CUPTI A/B against parent `b837f2f6825750ef2478efa6f5380ee8a47a573c`, whose only relevant difference is the exact `sm100a`/`sm103a` target: | SKU | Physical device | Family/exact, all 12 | Family/exact, six H12 | Family/official raw, all 12 | | --- | --- | ---: | ---: | ---: | | B200 | CC10.0 / 148 SM | 1.000277x | 1.000521x | 1.407313x | | GB200 | CC10.0 / 152 SM | 1.000451x | 1.000943x | 1.442069x | | B300 | CC10.3 / 148 SM | 1.000968x | 1.006770x | 1.414984x | | GB300 | CC10.3 / 152 SM | 0.997368x | 0.999521x | 1.453974x | All 48 family-target benchmark rows passed BF16 correctness. The sub-percent aggregate spread is measurement noise rather than a target regression, so the family cubin is retained. The complete test suite also covers supported shapes outside the 29-row performance matrix (including H2, H6, H12, H64, H96, fixed/packed, non-default streams, graph replay, strided beta, indexed state, and checkpoints); the matrix is not a runtime allowlist. Ineligible public inputs continue through the existing backend fallback instead of failing in the optimized native route. The final Cake head is `d6e91ce486af89f151de27e7243f3d8146a0e176`. Its kernel/codegen content is unchanged from GPU-qualified `e1782b2feb`, so the 29-shape kernel, regression, and six-focus results below remain the final kernel denominator. Exact-head sanitizer results are reported separately in the validation comment. ## Kernel performance All measurements below are same-device, order-balanced, cold-L2 CUPTI runs. JIT, allocation, metadata preparation, and state reset are outside the timed region. The baseline is pinned official raw FlashKDA `1ce47ea3`. | SKU | Arch / physical SMs | 29-shape geomean | Comparable 28-shape geomean | Six focus shapes geomean | Historical focus geomean | | --- | --- | ---: | ---: | ---: | ---: | | B200 | `sm_100a` / 148 | 1.867189x | 1.913946x | 2.070866x | 2.0653x | | GB200 | `sm_100a` / 152 | 1.971826x | 1.977442x | 2.132430x | 2.0746x | | B300 | `sm_103a` / 148 | 1.950843x | 2.009757x | 2.053773x | 2.0924x | | GB300 | `sm_103a` / 152 | 1.980640x | 1.989413x | 2.094719x | 2.0921x | The 29-shape aggregate now includes the repaired N128 holdout. Its two 148-SM exact-N16 rows are correctness-first and slower than raw official FlashKDA (`0.934181x` B200, `0.848068x` B300); GB200 and GB300 are `1.820890x` and `1.750093x`. Excluding only that newly added row gives the like-for-like 28-shape column above. All six-shape geomeans remain at or above the approximately 2.05x objective. The B300 final run is 1.846% below its older single-run ratio but 0.3135% faster than the same-device Cake baseline; the other historical deltas are +0.270%/+2.788%/+0.125% on B200/GB200/GB300. Detailed per-shape candidate/baseline latency and route tables are posted in the final validation comment. ## Previous whole-model E2E (exact-target artifact) Because the final export changes the GB300 cubin target, the same official 8xGB300/TP8 A/B is being rerun against `83ed637d`; the results below are retained only as the exact-target historical reference until replacement. The final official `moonshotai/Kimi-K3` run used FlashInfer `597b3518cbc036f11c2d3d264d18c41e7caeb6a9` and SGLang `2984c14c596cfdee6978af44b7215f3357510e77`, with TP8 on the same 8x GB300 NVL72 setup for both arms. Radix `extra_buffer`, `mamba_track_interval=2`, 2,048-token chunked prefill, and Triton decode were held fixed; only KDA prefill changed. - GSM8K 200: Triton 196/200 (`98.0%`), Cake 198/200 (`99.0%`); fixed outputs 6/6 exact. - TTFT speedup at 129/513/2,049/8,193/16,385 tokens: `0.9827x / 0.9830x / 1.0311x / 1.0699x / 1.0476x` (`1.0223x` geomean). - Throughput at 2,048 input / 64 output / concurrency 32: `5147.98 -> 5590.95 tokens/s` (`1.0860x`). - Triton-controlled decode remained neutral: `0.9953x` and `0.9980x` TPOT ratios for 128- and 256-output controls. - Route audit: 276,552 direct Cake prefill successes; 1,104 intentional `t1_decode_shape` fallbacks; zero unexpected fallback, malformed event, or fatal outcome. Full three-run evidence is posted on [sgl-project/sglang#34299](sgl-project/sglang#34299 (comment)). ## Current source identity - HEAD and GPU-qualified FlashInfer source: `83ed637db8912856f44900b938df1e2e335d2002` - Final Cake MR head: `d6e91ce486af89f151de27e7243f3d8146a0e176` - GPU-qualified Cake kernel runtime: `e1782b2feb65590d20ec0c67e8d42ac3ec18321a` - Frozen device-source origin: `f4b7f427af886312529c718bc4c8de1a2848ad56` - SGLang integration head: `2984c14c596cfdee6978af44b7215f3357510e77` The two packed-decode benchmark files intentionally cover different public/native implementations. --------- Co-authored-by: Yingyi Huang <averyh@nvidia.com>
…de (flashinfer-ai#4445) Related to flashinfer-ai#4254. Supersedes flashinfer-ai#4378. Adds native Blackwell recurrent-KDA prefill and packed T=1 decode support, including the serving contracts used by [sgl-project/sglang#34299](sgl-project/sglang#34299). The public backend name remains `cake_kda`. ## API behavior - `flashinfer.recurrent_kda` automatically dispatches eligible BF16 fixed or packed-varlen prefill on SM100a/SM103a to the frozen FlashKDA kernels while preserving existing decode behavior. - `beta` may be a positive, non-overlapping token-row-strided view with unit head stride. Indexed recurrent-state pools, padded physical slot strides, native intermediate checkpoints, caller-owned output, exact state aliasing, current-stream execution, and CUDA Graph workspace reuse are supported. - `flashinfer.kda_decode.recurrent_kda(..., backend="cake")` explicitly selects the exported Cake decode backend. `packed_kda_decode` consumes serving-native packed Kimi-K3 tensors and caller-owned indexed state. ## Export and routing This PR is the complete export of Cake runtime `e1782b2feb65590d20ec0c67e8d42ac3ec18321a`. The frozen device sources are from Cake `f4b7f427af886312529c718bc4c8de1a2848ad56`; the functional changes are host/binding routing repairs, so the exported CUDA is unchanged. Current HEAD `83ed637db8912856f44900b938df1e2e335d2002` contains the review follow-ups: CUDA 12.9+ uses one `sm100f` family cubin for CC10.0 and CC10.3, CUDA 12.8 CC10.0 retains `sm100a`, persistent routing remains limited to physical CC10.0 with a measured 148/152-SM count, shared tensor-check utilities replace local duplicates, the benchmark summarizer has its clearer name, and the synthetic benchmark-report test is removed. Public generated identifiers and comments use standalone FlashKDA terminology. No SHA-256 identity gate or test is part of the export. The 29-shape matrix below is a validation denominator, not a runtime allowlist; other calls satisfying the documented frozen prefill contract also route to Cake, while ordinary ineligible calls retain the existing backend fallback. The runtime caches `torch.cuda.get_device_properties(device).multi_processor_count` separately from the ISA target and passes it to route selection, persistent worker count, and LPT bin construction: - B200 `(sm_100a, 148 SM)` and GB200 `(sm_100a, 152 SM)` use persistent M128 for measured H96 mixed/uniform and H64 uniform packed shapes. - B300 `(sm_103a, 148 SM)` and GB300 `(sm_103a, 152 SM)` use direct M128. - Compatible fixed H64 uses M64; H12 uses direct N16. Strided beta, indexed state, and checkpoints remain on direct routes. - The H96 uniform N128 holdout uses exact direct N16 on the two 148-SM devices, persistent M128 on GB200, and direct M128 on GB300. ## Final four-SKU validation Final head `83ed637db8912856f44900b938df1e2e335d2002` passed on B200, GB200, B300, and GB300 with CUDA 13.3. Each device passed 43/43 JIT/import/AOT contracts and 83/83 GPU/API/stream/graph tests; all six recurrent-prefill and packed-decode modules built and loaded from `compute_100f,sm_100f` cubins. The public dispatcher used persistent routes only on CC10.0 and direct routes on CC10.3, independently of the physical 148/152-SM count. The family-target decision used same-device, order-balanced, cold-L2 CUPTI A/B against parent `b837f2f6825750ef2478efa6f5380ee8a47a573c`, whose only relevant difference is the exact `sm100a`/`sm103a` target: | SKU | Physical device | Family/exact, all 12 | Family/exact, six H12 | Family/official raw, all 12 | | --- | --- | ---: | ---: | ---: | | B200 | CC10.0 / 148 SM | 1.000277x | 1.000521x | 1.407313x | | GB200 | CC10.0 / 152 SM | 1.000451x | 1.000943x | 1.442069x | | B300 | CC10.3 / 148 SM | 1.000968x | 1.006770x | 1.414984x | | GB300 | CC10.3 / 152 SM | 0.997368x | 0.999521x | 1.453974x | All 48 family-target benchmark rows passed BF16 correctness. The sub-percent aggregate spread is measurement noise rather than a target regression, so the family cubin is retained. The complete test suite also covers supported shapes outside the 29-row performance matrix (including H2, H6, H12, H64, H96, fixed/packed, non-default streams, graph replay, strided beta, indexed state, and checkpoints); the matrix is not a runtime allowlist. Ineligible public inputs continue through the existing backend fallback instead of failing in the optimized native route. The final Cake head is `d6e91ce486af89f151de27e7243f3d8146a0e176`. Its kernel/codegen content is unchanged from GPU-qualified `e1782b2feb`, so the 29-shape kernel, regression, and six-focus results below remain the final kernel denominator. Exact-head sanitizer results are reported separately in the validation comment. ## Kernel performance All measurements below are same-device, order-balanced, cold-L2 CUPTI runs. JIT, allocation, metadata preparation, and state reset are outside the timed region. The baseline is pinned official raw FlashKDA `1ce47ea3`. | SKU | Arch / physical SMs | 29-shape geomean | Comparable 28-shape geomean | Six focus shapes geomean | Historical focus geomean | | --- | --- | ---: | ---: | ---: | ---: | | B200 | `sm_100a` / 148 | 1.867189x | 1.913946x | 2.070866x | 2.0653x | | GB200 | `sm_100a` / 152 | 1.971826x | 1.977442x | 2.132430x | 2.0746x | | B300 | `sm_103a` / 148 | 1.950843x | 2.009757x | 2.053773x | 2.0924x | | GB300 | `sm_103a` / 152 | 1.980640x | 1.989413x | 2.094719x | 2.0921x | The 29-shape aggregate now includes the repaired N128 holdout. Its two 148-SM exact-N16 rows are correctness-first and slower than raw official FlashKDA (`0.934181x` B200, `0.848068x` B300); GB200 and GB300 are `1.820890x` and `1.750093x`. Excluding only that newly added row gives the like-for-like 28-shape column above. All six-shape geomeans remain at or above the approximately 2.05x objective. The B300 final run is 1.846% below its older single-run ratio but 0.3135% faster than the same-device Cake baseline; the other historical deltas are +0.270%/+2.788%/+0.125% on B200/GB200/GB300. Detailed per-shape candidate/baseline latency and route tables are posted in the final validation comment. ## Previous whole-model E2E (exact-target artifact) Because the final export changes the GB300 cubin target, the same official 8xGB300/TP8 A/B is being rerun against `83ed637d`; the results below are retained only as the exact-target historical reference until replacement. The final official `moonshotai/Kimi-K3` run used FlashInfer `597b3518cbc036f11c2d3d264d18c41e7caeb6a9` and SGLang `2984c14c596cfdee6978af44b7215f3357510e77`, with TP8 on the same 8x GB300 NVL72 setup for both arms. Radix `extra_buffer`, `mamba_track_interval=2`, 2,048-token chunked prefill, and Triton decode were held fixed; only KDA prefill changed. - GSM8K 200: Triton 196/200 (`98.0%`), Cake 198/200 (`99.0%`); fixed outputs 6/6 exact. - TTFT speedup at 129/513/2,049/8,193/16,385 tokens: `0.9827x / 0.9830x / 1.0311x / 1.0699x / 1.0476x` (`1.0223x` geomean). - Throughput at 2,048 input / 64 output / concurrency 32: `5147.98 -> 5590.95 tokens/s` (`1.0860x`). - Triton-controlled decode remained neutral: `0.9953x` and `0.9980x` TPOT ratios for 128- and 256-output controls. - Route audit: 276,552 direct Cake prefill successes; 1,104 intentional `t1_decode_shape` fallbacks; zero unexpected fallback, malformed event, or fatal outcome. Full three-run evidence is posted on [sgl-project/sglang#34299](sgl-project/sglang#34299 (comment)). ## Current source identity - HEAD and GPU-qualified FlashInfer source: `83ed637db8912856f44900b938df1e2e335d2002` - Final Cake MR head: `d6e91ce486af89f151de27e7243f3d8146a0e176` - GPU-qualified Cake kernel runtime: `e1782b2feb65590d20ec0c67e8d42ac3ec18321a` - Frozen device-source origin: `f4b7f427af886312529c718bc4c8de1a2848ad56` - SGLang integration head: `2984c14c596cfdee6978af44b7215f3357510e77` The two packed-decode benchmark files intentionally cover different public/native implementations. --------- Co-authored-by: Yingyi Huang <averyh@nvidia.com>
Description
This PR adds a serving-native CAKE KDA decode backend behind a new independent
flashinfer.packed_kda_decodeAPI. It consumes post-convolution packed QKV,raw gate and beta logits, and an indexed BF16 recurrent-state pool directly,
so serving integrations do not need to split/copy Q/K/V, preprocess gate/beta,
or gather/scatter state around the recurrent update.
This API is intentionally separate from
recurrent_kda(already-split inputs)and
fused_kda_decode(which also owns convolution and gated RMSNorm).Exact contract
T=1,H=HV=12,K=V=128, BF16 data/state;[B, 4608], raw gate[B, 1536], raw beta[B, 12];A_log[12]anddt_bias[1536];[N, 12, 128, 128], with compact inner dimensions and anarbitrary positive/disjoint outer slot stride;
contract, and
-1is an inactive graph-padding row;scale=1/sqrt(128), L2 epsilon1e-6, andlower_bound=-5semantics;[B, 1, 12, 128]and the caller's currentPyTorch CUDA stream.
The public call is strict: unsupported devices or tensor contracts raise and
never silently route to another implementation.
Frozen schedules and architecture boundary
B < 32: tile8 schedule, grid(192, B, 1);B >= 32: tile16 schedule, grid(96, B, 1);(32, 1, 1), zero dynamic shared memory;sm_100aand exactsm_103aJIT/AOT modules; nosm_100fclaim.The frozen generated bodies are protected by raw-body SHA-256 tests:
d0de8869242d09bf0c1c4840a7fd73dcd32835050cdc08db58b19a2c7506d0da;d8a446e42da47e2d8cd05139c77efe9c970f2d36394b68b49649beb6bc2bbfbe.Integration and tests
side effect, and API documentation;
strides, non-identity and
-1indices, untouched state/padding, currentstream, CUDA Graph changed-input/index replay, and a 512-step FP64 diagnostic;
digests.
Validation
PR head:
826ed5f2fdc301440aa1ec799066f2fa3dfb63d5.The performance-evidence commit is its direct parent
89a5d3fcee8f8ddcff9da5547d095ffd73c1ef79. The follow-up only pre-includesmath_constants.houtside the generated-type rename boundary and sorts aPython
__all__; it does not change either frozen body, the binding checks,launch parameters, or public API. The exact PR head was revalidated on both
architectures after that review follow-up.
Exported public-API correctness
sm_100a1.5259e-52.4414e-4sm_103a7.6294e-64.8828e-4Both targets cover the complete batch sweep, production strides, non-identity
and inactive indices, current-stream launches, CUDA Graph changed-input replay,
and the 512-step FP64 diagnostic.
Exported public API vs exact SGLang packed Triton
Median GPU span from strict CUPTI, cold L2, 30 rounds per backend, same inputs,
and alternating AB/BA order. The baseline is the exact raw SGLang packed Triton
serving kernel (source SHA-256
cf5e980d86174a631bcd0872f8ebf7a6ab7c21c3435c097f66f8ba0e686debc0),not a copied CAKE-internal timing table.
B200 /
sm_100aGB300 /
sm_103aEvery timed sample on both GPUs contained exactly one launch and one kernel
activity with zero inter-kernel gap. Across all batches and both modes, worst
output max abs was
1.5259e-5, worst state max abs was4.8828e-4, andunselected state slots and padding were bitwise preserved. Regressions at
GB300 B31/B32 and B200 graph B32 are retained rather than hidden by geomeans.
A same-configuration cross-run Kimi-K3 SGLang comparison reports CAKE output
throughput
1985.439 tok/sversus the recorded Triton baseline1979.075 tok/s(1.0032x), while median TPOT is effectively flat(
30.814 msversus30.806 ms,0.9998x). GSM8K medians are0.975forCAKE and
0.980for Triton, passing the configured quality gate. Because themeasurements use different source snapshots and allocations and the delta is
noise-scale, they do not establish a material E2E speedup. Full runs and
methodology are in the SGLang PR.
SGLang integration: sgl-project/sglang#33647
Related to #4254.