Skip to content

fix(cake_kda): support non-aligned recurrent prefill head counts - #4351

Merged
yyihuang merged 1 commit into
flashinfer-ai:mainfrom
yyihuang:codex/kda-prefill-h12-export
Aug 5, 2026
Merged

yyihuang merged 1 commit into
flashinfer-ai:mainfrom
yyihuang:codex/kda-prefill-h12-export

Conversation

@yyihuang

@yyihuang yyihuang commented Aug 4, 2026

Copy link
Copy Markdown
Collaborator

Follow-up to merged #4262 and #4313.

What changed

  • pad the beta-only TMA source to round_up(H, 8) instead of only padding H < 8;
  • teach the beta pack kernel and binding validation to use the dynamic padded-head stride;
  • retain the caller-visible H, state shape, launch grid, and frozen M64/M128 CUDA bodies unchanged;
  • add H=12 eager, packed, full-chunk-plus-tail, final-state, and CUDA graph replay coverage.

The frozen kernels load beta in 8-head TMA boxes. For H=12, the original BF16 row stride is 24 bytes and cuTensorMapEncodeTiled rejects it. Padding the descriptor source to 16 heads gives a 32-byte row stride; heads 12–15 are padding only and are never assigned CTAs.

This generalizes the fix to every positive head count that is not divisible by eight, while aligned head counts retain the existing zero-copy beta path.

Correctness

Both runs used the public flashinfer.recurrent_kda facade and compared BF16 output plus the complete final state against the PyTorch reference with atol=rtol=1e-2.

GPU Compute capability CUDA Result
NVIDIA B200 10.0 12.9 62 passed, 0 skipped, 0 failed
NVIDIA GB300 10.3 12.9 62 passed, 0 skipped, 0 failed

The test gate runs:

tests/jit/test_flash_kda_jit.py
tests/kda/test_recurrent_kda_prefill.py

H=12 coverage includes fixed T=32, fixed T=33 (one full TMA chunk plus the direct-load tail), packed sequence lengths [32, 3], in-place initial/final state, and CUDA graph replay after beta is changed. The JIT contract test also verifies that the frozen generated M64/M128 bodies remain unchanged.

pre-commit run --files <changed files> passes.

Performance

The H=12 path was benchmarked through the public flashinfer.recurrent_kda facade with fallback forbidden. Speedup is the official FlashKDA raw GPU span divided by this PR's public-API GPU span. The baseline is the same official FlashKDA source used for #4262: MoonshotAI/FlashKDA@d2ff19a, with CUTLASS 5c149f5.

Measurements use strict CUPTI first-to-last correlated compute-kernel span, cold L2, no CUDA Graph, and two independent 128-sample blocks in symmetric ABCCBA order. The PR span includes both the beta pack and frozen M128 recurrence kernels. All six benchmark shapes passed output and complete-final-state correctness against the official peer with BF16 atol=rtol=1e-2.

H=12 shape SM100 / B200: PR / baseline Speedup SM103 / GB300: PR / baseline Speedup
packed [512] x 32 136.159 / 240.239 us 1.7644x 128.264 / 233.496 us 1.8204x
packed [128] x 8 23.760 / 46.448 us 1.9549x 25.712 / 52.904 us 2.0576x
fixed [512] 46.184 / 76.383 us 1.6539x 47.712 / 82.240 us 1.7237x
fixed [8192] 514.197 / 814.435 us 1.5839x 487.161 / 779.426 us 1.5999x
mixed [1300, 547, 2048, 963, 271, 3063] 208.647 / 351.550 us 1.6849x 198.216 / 340.913 us 1.7199x
uniform [1024] x 8 82.080 / 162.703 us 1.9823x 78.440 / 162.505 us 2.0717x
Six-shape geometric mean 1.7645x 1.8238x
Comparison Result
FlashInfer upstream main at H=12 unsupported / N/A

Upstream main fails before kernel launch with cuTensorMapEncodeTiled failed for beta_tma with CUresult=1, so there is no valid upstream H=12 timing or speedup claim. This PR leaves the frozen compute kernel unchanged; it adds the required beta packing only for non-8-aligned head counts.

Related to #4254.

Summary by CodeRabbit

  • Bug Fixes

    • Improved beta padding for head counts that are not divisible by eight.
    • Ensured packed inputs correctly handle larger, non-aligned head counts.
    • Added validation for padded storage requirements.
  • Documentation

    • Clarified beta padding behavior and public tensor shapes.
  • Tests

    • Expanded coverage for 12-head inputs, varied sequence lengths, chunk boundaries, packed inputs, and CUDA graph updates.

Pad beta TMA rows to the next eight-head boundary and teach the binding pack kernel to use the dynamic padded stride. Add H=12 eager, packed, full-plus-tail, and CUDA graph correctness coverage.\n\nAI-assisted-by: OpenAI Codex
@coderabbitai

coderabbitai Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

FlashKDA beta TMA padding now rounds head counts to multiples of eight. C++ validation and packing use the rounded width and full padded storage. Documentation and recurrent prefill tests cover non-aligned head counts.

Changes

Beta TMA padding and packing

Layer / File(s) Summary
Padding contract
flashinfer/kda_prefill.py, docs/api/kda_prefill.rst
Beta TMA heads now round up to the next multiple of eight. Public beta and state shapes retain the logical head count.
Validation and packing path
csrc/kda/flashkda_binding_common.cuh
Validation requires the rounded width. The launcher and packing kernel use the full padded storage for non-aligned head counts.
Contract and prefill coverage
tests/jit/test_flash_kda_jit.py, tests/kda/test_recurrent_kda_prefill.py
Tests cover 12-head fixed and packed inputs, TMA chunk boundaries, workspace indexing, and CUDA graph beta refreshes for non-aligned head counts.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant BetaTmaSource
  participant BetaTmaValidation
  participant BetaTmaLauncher
  participant BetaPackingKernel
  BetaTmaSource->>BetaTmaValidation: provide rounded beta TMA width
  BetaTmaValidation->>BetaTmaLauncher: validate padded storage
  BetaTmaLauncher->>BetaPackingKernel: pass padded dimensions and storage extent
  BetaPackingKernel->>BetaTmaLauncher: pack beta TMA storage
Loading

Possibly related PRs

Suggested labels: op: attention

Suggested reviewers: yzh119, kahyunnam, aleozlx

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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 summarizes the main change: support for non-aligned recurrent prefill head counts.
Description check ✅ Passed The description clearly explains the changes, rationale, tests, performance, and related issue, although it does not reproduce every template heading or checklist item.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

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

Actionable comments posted: 1

🤖 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 `@csrc/kda/flashkda_binding_common.cuh`:
- Line 243: Update the beta TMA packing guards around beta_tma_heads and the
PackBetaForTmaKernel call to skip packing only when beta and beta_tma are
exactly aliased, applying the existing overlap exemptions only in that case. For
every separate beta_tma destination, pack its full numel rather than relying on
padded_num_heads. Add a regression case covering H=8 with fewer than 32 tokens.
🪄 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: d41a495b-e56a-473b-832a-eadb1e7c31c0

📥 Commits

Reviewing files that changed from the base of the PR and between 0961af9 and c91f8d9.

📒 Files selected for processing (5)
  • csrc/kda/flashkda_binding_common.cuh
  • docs/api/kda_prefill.rst
  • flashinfer/kda_prefill.py
  • tests/jit/test_flash_kda_jit.py
  • tests/kda/test_recurrent_kda_prefill.py

"storage";
CheckNoPartialOverlapOrExactAlias(beta, "beta", beta_tma, "beta_tma");
if (num_heads < kBetaTmaMinHeads) {
if (beta_tma_heads != num_heads) {

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.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Use exact storage aliasing to decide whether to skip packing.

When H is divisible by eight and token_count < 32, flashinfer/kda_prefill.py allocates a separate [32, H] beta_tma workspace. padded_num_heads == num_heads is then true, but beta_tma does not contain beta values. Line 325 skips PackBetaForTmaKernel, and the frozen kernel reads uninitialized workspace values.

Use exact beta/beta_tma storage aliasing for this early return. Apply the overlap exemptions at Line 243 only for that exact alias. Pack the full beta_tma.numel() for every separate destination. Add a regression case with H=8 and fewer than 32 tokens.

Proposed fix
-  if (beta_tma_heads != num_heads) {
+  const bool beta_tma_exact_alias =
+      beta.data_ptr() == beta_tma.data_ptr() && beta.numel() == beta_tma.numel();
+  if (!beta_tma_exact_alias) {
     CheckNoOverlap(beta_tma, "beta_tma", q, "q");
     // ...
   }

-  if (padded_num_heads == num_heads) {
+  const bool beta_tma_exact_alias =
+      beta.data_ptr() == beta_tma.data_ptr() && beta.numel() == beta_tma.numel();
+  if (beta_tma_exact_alias) {
     return;
   }

Also applies to: 324-326

🤖 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 `@csrc/kda/flashkda_binding_common.cuh` at line 243, Update the beta TMA
packing guards around beta_tma_heads and the PackBetaForTmaKernel call to skip
packing only when beta and beta_tma are exactly aliased, applying the existing
overlap exemptions only in that case. For every separate beta_tma destination,
pack its full numel rather than relying on padded_num_heads. Add a regression
case covering H=8 with fewer than 32 tokens.

@yzh119 yzh119 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Any changes in terms of performance?

@yzh119 yzh119 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Any changes on performance?

@yyihuang

yyihuang commented Aug 5, 2026

Copy link
Copy Markdown
Collaborator Author

/bot run tests/kda

@yyihuang
yyihuang enabled auto-merge (squash) August 5, 2026 05:43
@yyihuang
yyihuang merged commit 38bf507 into flashinfer-ai:main Aug 5, 2026
33 checks passed
@yyihuang
yyihuang deleted the codex/kda-prefill-h12-export branch August 6, 2026 01:06
@yyihuang
yyihuang restored the codex/kda-prefill-h12-export branch August 6, 2026 01:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

op: linear attention KDA, mamba, GDN, etc. review filtering.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants