Skip to content

perf(attention): reuse TRT-LLM-gen counter buffers - #3949

Merged
qsang-nv merged 4 commits into
mainfrom
fix/mingyangw/nvbug-nvbug-6438060
Jul 15, 2026
Merged

qsang-nv merged 4 commits into
mainfrom
fix/mingyangw/nvbug-nvbug-6438060

Conversation

@saltyminty

@saltyminty saltyminty commented Jul 13, 2026 •

Copy link
Copy Markdown
Collaborator

📌 Description

Fix the B300 TRT-LLM-native decode regression caused by allocating and zeroing the multi-CTA KV counter sidecar inside every timed call.

  • Preallocate separate reusable multi-CTA counter buffers for the direct GQA and MLA benchmark paths before timing and CUDA graph capture.
  • Add optional caller-owned counter-buffer reuse to the TRT-LLM-gen MLA API. Supplied buffers are validated for device, contiguity, and capacity; callers that omit the buffer retain the internal allocation fallback.
  • Keep caller-owned MLA buffers out of synthetic autotune profiles. The TRT-LLM MLA runner lazily allocates and reuses runner-owned profiling storage, then receives the caller buffer only for the selected runner's final real request. This allows autotune profile batches to exceed the request batch without resizing or replacing caller storage.
  • Add exact GPT-OSS and MLA regression coverage for repeated reuse, pointer stability, kernel self-reset, invalid buffers, and the autotune profiling/final-call ownership contract.

Fresh validation at PR head 78b622da reran the exact B300/SM103 GPT-OSS case against the pre-regression parent using one warm process and five measured processes per ref. Median TRT-LLM-native throughput was 15.330 TFLOP/s for this PR versus 15.313 TFLOP/s for the parent. Reconstructed from the fixed operation count, latency was 0.008755233 ms versus 0.008764953 ms, making this PR 0.110894% faster. All 12 benchmark processes passed refcheck; both refs used the same CUDA-event fallback because CUPTI was unavailable.

🔍 Related Issues

🚀 Pull Request Checklist

Thank you for contributing to FlashInfer! Before we review your pull request, please make sure the following items are complete.

✅ Pre-commit Checks

  • I have installed pre-commit by running pip install pre-commit (or used your preferred method).
  • I have installed the hooks with pre-commit install.
  • I have run the hooks manually with pre-commit run --all-files and fixed any reported issues.

If you are unsure about how to set up pre-commit, see the pre-commit documentation.

🧊 Tests

  • Tests have been added or updated as needed.
  • All tests are passing (unittest, etc.).

Validation includes the focused B300 correctness suite (28 passed), the broader decode/MLA/prefill regression matrix (1392 passed), focused B200 caller-buffer validation (6 passed), targeted B200 autotune/final-call validation (7 passed), and the fresh exact B300 parent-versus-current benchmark above. Repository and changed-file pre-commit coverage passed, including mypy and Ruff.

Reviewer Notes

Please focus on the caller-owned buffer lifetime and autotune ownership contract. Synthetic autotune profiles use reusable runner-owned storage so profile batch buckets can exceed the real request batch safely. Only the selected TRT-LLM runner's final request receives caller-owned storage. Caller buffers must remain alive for all launches or graph replays and must not be shared by overlapping streams or graphs.

Summary by CodeRabbit

  • New Features
    • Added optional multi-CTA KV counter buffer support for TRT-LLM attention and MLA decoding.
    • Caller-provided buffers can be reused across decode runs, including autotuned execution.
    • Added backend-aware handling to ensure the counter buffer is only applied where supported.
  • Bug Fixes
    • Improved correctness when using preallocated output tensors for MLA decoding (including in-place behavior).
  • Tests
    • Expanded tests to verify counter-buffer reuse, all-zero preservation, validation errors, and autotune behavior.

Preallocate the direct GQA and MLA benchmark counter sidecars outside timed calls. Add caller-owned MLA buffer reuse with validation and regression coverage for repeated launches.
@coderabbitai

coderabbitai Bot commented Jul 13, 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: 24210221-28f4-4640-897a-c8bc563b19db

ðŸ“Ĩ Commits

Reviewing files that changed from the base of the PR and between f919c89 and 002920b.

📒 Files selected for processing (5)
  • flashinfer/decode.py
  • flashinfer/mla/_core.py
  • flashinfer/prefill.py
  • flashinfer/utils.py
  • tests/attention/test_trtllm_gen_mla.py
🚧 Files skipped from review as they are similar to previous changes (2)
  • tests/attention/test_trtllm_gen_mla.py
  • flashinfer/mla/_core.py

📝 Walkthrough

Walkthrough

The TRT-LLM decode and prefill paths now resolve reusable multi-CTA KV counter buffers through shared validation logic. MLA dispatch, benchmarks, autotuning, and tests support caller-provided buffers, repeated execution, output reuse, and invalid-buffer checks.

Changes

TRT-LLM multi-CTA counter buffer

Layer / File(s) Summary
Shared counter buffer resolution
flashinfer/utils.py, flashinfer/decode.py, flashinfer/prefill.py
A shared resolver allocates or validates counter buffers, and decode and prefill paths use it instead of duplicating sizing and validation logic.
MLA counter buffer API and runtime
flashinfer/mla/_core.py
MLA decode accepts optional counter buffers, resolves storage for TRT-LLM Gen execution, forwards buffers through autotuning, and rejects them for unsupported backends.
Benchmark counter buffer wiring
benchmarks/routines/attention.py
TRT-LLM native GQA and MLA benchmark paths allocate zeroed counter buffers and pass them to decode calls.
Counter reuse validation
tests/attention/test_trtllm_gen_attention_decode.py, tests/attention/test_trtllm_gen_mla.py, tests/autotuner/test_autotuner_mla_decode.py
Tests cover repeated decode calls, output storage reuse, zero-state preservation, autotuner forwarding, and undersized, non-contiguous, and misaligned buffers.

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

Sequence Diagram(s)

sequenceDiagram
  participant Caller
  participant trtllm_batch_decode_with_kv_cache_mla
  participant _resolve_trtllm_gen_multi_ctas_kv_counter_buffer
  participant TrtllmGenMlaDecodeRunner
  participant GPU
  Caller->>trtllm_batch_decode_with_kv_cache_mla: provide optional counter buffer
  trtllm_batch_decode_with_kv_cache_mla->_resolve_trtllm_gen_multi_ctas_kv_counter_buffer: validate or allocate buffer
  _resolve_trtllm_gen_multi_ctas_kv_counter_buffer-->>trtllm_batch_decode_with_kv_cache_mla: return resolved buffer
  trtllm_batch_decode_with_kv_cache_mla->>TrtllmGenMlaDecodeRunner: pass buffer for TRT-LLM execution
  TrtllmGenMlaDecodeRunner->>GPU: execute decode and reuse counter buffer
  GPU-->>Caller: return output
Loading

Possibly related PRs

Suggested labels: run-ci

Suggested reviewers: yzh119, aleozlx, yyihuang

ðŸšĨ 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%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title is concise and accurately captures the main change: reusing TRT-LLM-gen counter buffers for performance.
Description check ✅ Passed The description follows the template well, with summary, related issue, checklist, tests, and reviewer notes filled in.
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.
âœĻ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧊 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/mingyangw/nvbug-nvbug-6438060

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.

@gemini-code-assist gemini-code-assist 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.

Code Review

This pull request introduces support for a caller-owned, reusable multi_ctas_kv_counter_buffer in the trtllm-gen backend for both GQA and MLA decoding. It adds validation logic to ensure the buffer is contiguous, on the correct device, and of sufficient size, and integrates it into the decoding runners and API entry points. Benchmarks and unit tests have been updated to verify correct behavior and error handling. No review comments were provided, so there is no additional feedback.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

@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

ðŸ§đ Nitpick comments (1)
flashinfer/mla/_core.py (1)

2209-2235: 📐 Maintainability & Code Quality | ðŸ”ĩ Trivial | ðŸ’Ī Low value

Consider placing the resolver in flashinfer/utils.py for cross-module reuse.

The validation logic here (device match, contiguity, minimum byte size) duplicates the inline validation in flashinfer/prefill.py:251-274. Placing this function alongside get_trtllm_gen_multi_ctas_kv_counter_bytes and _get_trtllm_gen_multi_ctas_kv_counter_buffer in flashinfer/utils.py would let both paths share one implementation and keep the added contiguity check consistent everywhere.

ðŸĪ– 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/mla/_core.py` around lines 2209 - 2235, Move
_resolve_trtllm_gen_multi_ctas_kv_counter_buffer into flashinfer/utils.py
alongside get_trtllm_gen_multi_ctas_kv_counter_bytes and
_get_trtllm_gen_multi_ctas_kv_counter_buffer, then update both the _core.py and
prefill.py call paths to reuse it. Preserve the existing device, contiguity, and
minimum-byte validations consistently across both paths.
ðŸĪ– 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 `@flashinfer/mla/_core.py`:
- Around line 3136-3156: The MLA decode autotune path must not reuse a
caller-owned multi_ctas_kv_counter_buffer when profiling batch sizes larger than
query.size(0). Update the flow around
_resolve_trtllm_gen_multi_ctas_kv_counter_buffer and TrtllmGenMlaDecodeRunner to
fall back to an internally allocated buffer during autotune, or explicitly
reject/document the combination with flashinfer.autotune(True).

---

Nitpick comments:
In `@flashinfer/mla/_core.py`:
- Around line 2209-2235: Move _resolve_trtllm_gen_multi_ctas_kv_counter_buffer
into flashinfer/utils.py alongside get_trtllm_gen_multi_ctas_kv_counter_bytes
and _get_trtllm_gen_multi_ctas_kv_counter_buffer, then update both the _core.py
and prefill.py call paths to reuse it. Preserve the existing device, contiguity,
and minimum-byte validations consistently across both paths.
🊄 Autofix (Beta)

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

Run ID: 3e78cbb8-9e03-4a59-9ff5-a663d845e82f

ðŸ“Ĩ Commits

Reviewing files that changed from the base of the PR and between 78333e8 and cce39dc.

📒 Files selected for processing (4)
  • benchmarks/routines/attention.py
  • flashinfer/mla/_core.py
  • tests/attention/test_trtllm_gen_attention_decode.py
  • tests/attention/test_trtllm_gen_mla.py

Comment thread flashinfer/mla/_core.py Outdated
Use runner-owned counter storage for synthetic autotune profiles, then pass a caller-owned buffer only to the selected TRT-LLM runner's final request. Add focused coverage for profiling reuse and final-call buffer identity.

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

ðŸ§đ Nitpick comments (1)
tests/attention/test_trtllm_gen_mla.py (1)

1205-1249: 📐 Maintainability & Code Quality | ðŸ”ĩ Trivial | ⚡ Quick win

Decouple the buffer-validation assertions from the parametrized matrix
These ValueError checks only run for (batch_size=1, q_len_per_request=1), so coverage silently disappears if that combination is removed from the parametrization later. Move them into a dedicated non-parametrized test or make the sentinel case explicit.

ðŸĪ– 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/attention/test_trtllm_gen_mla.py` around lines 1205 - 1249, Move the
buffer-validation checks out of the batch-size/query-length parametrized test
and into a dedicated non-parametrized test, reusing the necessary setup for
query, KV cache, workspace, and counter buffers. Ensure the new test
independently covers both the undersized and non-contiguous
multi_ctas_kv_counter_buffer cases without relying on the (1, 1) sentinel
combination.
ðŸĪ– 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.

Nitpick comments:
In `@tests/attention/test_trtllm_gen_mla.py`:
- Around line 1205-1249: Move the buffer-validation checks out of the
batch-size/query-length parametrized test and into a dedicated non-parametrized
test, reusing the necessary setup for query, KV cache, workspace, and counter
buffers. Ensure the new test independently covers both the undersized and
non-contiguous multi_ctas_kv_counter_buffer cases without relying on the (1, 1)
sentinel combination.

â„đïļ Review info
⚙ïļ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 6a78f34d-c9ee-43f3-99f6-215057d58f18

ðŸ“Ĩ Commits

Reviewing files that changed from the base of the PR and between 78b622d and f919c89.

📒 Files selected for processing (3)
  • tests/attention/test_trtllm_gen_attention_decode.py
  • tests/attention/test_trtllm_gen_mla.py
  • tests/autotuner/test_autotuner_mla_decode.py
ðŸ’Ī Files with no reviewable changes (1)
  • tests/attention/test_trtllm_gen_attention_decode.py

@qsang-nv

Copy link
Copy Markdown
Collaborator

Verified the autotune ownership contract at head f919c89f: choose_one is invoked without extra kwargs, so profiling never sees the caller buffer (runner-owned storage grows instead), and only the final real call receives it. No blockers from my side. A few non-blocking suggestions:

  1. Alignment validation gap: the launcher requires the counter pointer to be 16-byte aligned (csrc/trtllm_fmha_kernel_launcher.cu:242), but _resolve_trtllm_gen_multi_ctas_kv_counter_buffer only checks device/contiguity/size. A contiguous-but-offset view (e.g. torch.zeros(n + 1, dtype=torch.uint8, device="cuda")[1:]) passes Python validation and only fails at the kernel boundary — under autotune that means the final real request, after tuning already succeeded. Suggest adding a data_ptr() % 16 == 0 check that raises ValueError, plus a "contiguous but offset" sibling of the existing strided-view test.
  2. Share the resolver: decode.py and prefill.py carry duplicated inline validation that lacks the contiguity (and alignment) checks the MLA resolver has. Moving the resolver into flashinfer/utils.py next to get_trtllm_gen_multi_ctas_kv_counter_bytes and reusing it in all three paths would fix the asymmetry once.
  3. Orphaned comment: in TrtllmGenMlaDecodeRunner.__init__, the "Allocated lazily for autotune profilingâ€Ķ" comment now dangles after self.lse = lse, while the attribute it documents (_multi_ctas_kv_counter_buffer) moved up next to self.sm_count. Move the comment next to the attribute.
  4. Nit: in forward(), the None branch calls _resolve_trtllm_gen_multi_ctas_kv_counter_buffer(None, ...), which is just _get_trtllm_gen_multi_ctas_kv_counter_buffer with extra indirection — call it directly.

@saltyminty

Copy link
Copy Markdown
Collaborator Author

/bot run tests/attention tests/autotuner/test_autotuner_mla_decode.py

@flashinfer-bot

Copy link
Copy Markdown
Collaborator

GitLab MR !962 has been created, and the CI pipeline #58004181 is currently running. I'll report back once the pipeline job completes.

@flashinfer-bot

Copy link
Copy Markdown
Collaborator

[FAILED] Pipeline #58004181: 14/20 passed

@qsang-nv
qsang-nv merged commit 052a159 into main Jul 15, 2026
32 of 55 checks passed
@qsang-nv
qsang-nv deleted the fix/mingyangw/nvbug-nvbug-6438060 branch July 15, 2026 02:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants