Skip to content

[TRTLLM-12714][fix] Free CUDA-graph padding dummy KV caches before pool rebalance adjust() - #16157

Open
thorjohnsen wants to merge 3 commits into
NVIDIA:mainfrom
thorjohnsen:fix/kv-rebalance-cuda-graph-padding-dummy
Open

[TRTLLM-12714][fix] Free CUDA-graph padding dummy KV caches before pool rebalance adjust()#16157
thorjohnsen wants to merge 3 commits into
NVIDIA:mainfrom
thorjohnsen:fix/kv-rebalance-cuda-graph-padding-dummy

Conversation

@thorjohnsen

@thorjohnsen thorjohnsen commented Jul 9, 2026

Copy link
Copy Markdown
Collaborator

Background

KvCacheConfig.enable_kv_pool_rebalance (the KVCacheManagerV2 auto-tuner hook, #14578) suspends every request in PyExecutor.active_requests before calling adjust(), which asserts that all living KV caches are suspended.

CudaGraphRunner._pad_batch creates persistent padding dummy requests whose V2 KV caches stay ACTIVE across iterations but never appear in active_requests. The hook therefore never suspends them, and the first live adjust() fails its precondition assert, terminating the executor event loop with all in-flight requests errored. CUDA-graph padding is enabled by default, so any deployment of the flag on a multi-pool-group model (e.g. gemma-3 with VSWA max_attention_window) hits this within the tuner's first adjustment (~2 minutes of serving).

Summary

  • _maybe_rebalance_kv_pools: close the padding dummies (mgr.free_resources) and clear runner.padding_dummy_requests before the suspend loop; _pad_batch lazily recreates them on the next padded batch with the post-adjust pool layout. Mirrors the existing reset idiom in ModelEngine.warmup and CudaGraphRunner.clear.
  • Regression unit test test_frees_cuda_graph_padding_dummies_before_adjust (verifies the dummies are freed before adjust() runs and the map is cleared).

Impact

  • Only affects the opt-in prototype rebalance path (enable_kv_pool_rebalance=True, default off); no behavior change otherwise.
  • Verified live on 1x H100 with gemma-3-1b-it (VSWA, 2 pool groups): 9 rebalances across three benchmark runs — including one under TLLM_KV_CACHE_MANAGER_V2_DEBUG=1 — with no assertion failures. tests/unittest/_torch/executor/test_kv_pool_rebalance.py: 16/16 pass.

Test Coverage

  • tests/unittest/_torch/executor/test_kv_pool_rebalance.py::TestMaybeRebalanceKvPools::test_frees_cuda_graph_padding_dummies_before_adjust

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes
    • Improved KV-pool rebalance handling so unused CUDA graph padding requests are cleaned up before rebalancing.
    • Freed temporary request resources earlier and cleared pending padding entries to avoid stale state during execution adjustments.
    • Added coverage to prevent regressions in the rebalance flow and ensure cleanup happens in the correct order.

…ol rebalance adjust()

CudaGraphRunner._pad_batch creates persistent padding dummy requests
whose KVCacheManagerV2 caches stay ACTIVE across iterations but never
appear in PyExecutor.active_requests. The rebalance hook therefore
never suspends them, and the first live adjust() fails its
all-caches-suspended precondition assert, terminating the executor
event loop with all in-flight requests. CUDA-graph padding is enabled
by default, so any deployment of enable_kv_pool_rebalance on a
multi-pool-group model (e.g. gemma-3 with VSWA) hits this within the
tuner's first adjustment.

Close the padding dummies before suspending and clear
padding_dummy_requests so _pad_batch lazily recreates them with the
post-adjust pool layout, mirroring the existing reset idiom in
ModelEngine.warmup and CudaGraphRunner.clear.

Verified live on 1x H100 with gemma-3-1b-it (VSWA, 2 pool groups):
9 rebalances across three benchmark runs, including one run under
TLLM_KV_CACHE_MANAGER_V2_DEBUG=1, with no assertion failures.

Signed-off-by: Thor Johnsen <41591019+thorjohnsen@users.noreply.github.com>
@thorjohnsen
thorjohnsen requested a review from a team as a code owner July 9, 2026 00:24
@thorjohnsen
thorjohnsen requested a review from dongxuy04 July 9, 2026 00:24
@thorjohnsen

Copy link
Copy Markdown
Collaborator Author

/bot run

@coderabbitai

coderabbitai Bot commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

The KV pool rebalance hook in PyExecutor now frees CUDA-graph padding dummy requests' KV resources and clears the padding dummy map before suspending active requests and calling adjust. A test fixture default and a new regression test validate this cleanup order and behavior.

Changes

KV pool rebalance padding dummy cleanup

Layer / File(s) Summary
Rebalance hook frees padding dummy resources
tensorrt_llm/_torch/pyexecutor/py_executor.py
_maybe_rebalance_kv_pools now detects cuda_graph_runner.padding_dummy_requests, frees each dummy's KV resources via free_resources, and clears the map before proceeding with suspension and mgr.impl.adjust().
Fixture default and regression test
tests/unittest/_torch/executor/test_kv_pool_rebalance.py
_make_executor initializes padding_dummy_requests to an empty dict; a new test verifies dummies are freed exactly once, the map is cleared, and freeing occurs before adjust.

Estimated code review effort: 2 (Simple) | ~10 minutes

Sequence Diagram(s)

sequenceDiagram
  participant PyExecutor
  participant CudaGraphRunner
  participant KvCacheManager
  PyExecutor->>CudaGraphRunner: check padding_dummy_requests
  loop each dummy request
    PyExecutor->>KvCacheManager: free_resources(dummy)
  end
  PyExecutor->>CudaGraphRunner: clear padding_dummy_requests
  PyExecutor->>KvCacheManager: impl.adjust()
Loading
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title is specific, concise, and accurately reflects the main fix and scope of the PR.
Description check ✅ Passed The description is mostly complete, with clear Background, Summary, Impact, and Test Coverage sections matching the template intent.
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.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

🧹 Nitpick comments (1)
tests/unittest/_torch/executor/test_kv_pool_rebalance.py (1)

206-235: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Good regression coverage for the free-before-adjust ordering.

The test correctly exercises the fix's core contract: free_resources is called exactly once with the dummy, padding_dummy_requests ends up empty, and the free happens before adjust(). One gap: there's no test for the runner is None (no cuda_graph_runner attribute) or multi-dummy-map case; the current fixture always provides a MagicMock runner with a dict, so that guard branch (if runner is not None and runner.padding_dummy_requests:) is untested. Given it's a trivial getattr/dict-truthiness guard, this is a minor coverage gap rather than a blocker — consider adding it if you want full branch coverage, but not required for this PR.

As per path instructions, "Act as a QA engineer reviewing test changes and coverage... suggest concrete list file names and whether coverage is sufficient, insufficient, or needs follow-up outside the PR": coverage here is sufficient for the fix's primary contract; the runner is None branch is an optional follow-up.

🤖 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/unittest/_torch/executor/test_kv_pool_rebalance.py` around lines 206 -
235, Add a small follow-up test around PyExecutor._maybe_rebalance_kv_pools to
cover the guard path where cuda_graph_runner is None or where
padding_dummy_requests is empty, since the current test only exercises the
non-empty MagicMock runner case. Reuse the existing _make_executor and
_make_request helpers, but set model_engine.cuda_graph_runner to None (or an
empty dummy map) and assert the rebalance path still runs without trying to free
padding dummies. This will cover the runner-null/dict-truthiness branch in the
same area as test_frees_cuda_graph_padding_dummies_before_adjust.

Source: Path instructions

🤖 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/unittest/_torch/executor/test_kv_pool_rebalance.py`:
- Around line 206-235: Add a small follow-up test around
PyExecutor._maybe_rebalance_kv_pools to cover the guard path where
cuda_graph_runner is None or where padding_dummy_requests is empty, since the
current test only exercises the non-empty MagicMock runner case. Reuse the
existing _make_executor and _make_request helpers, but set
model_engine.cuda_graph_runner to None (or an empty dummy map) and assert the
rebalance path still runs without trying to free padding dummies. This will
cover the runner-null/dict-truthiness branch in the same area as
test_frees_cuda_graph_padding_dummies_before_adjust.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 34f50a4b-a97f-4d3f-b709-faf252a59d14

📥 Commits

Reviewing files that changed from the base of the PR and between 4cd00bb and 236f52c.

📒 Files selected for processing (2)
  • tensorrt_llm/_torch/pyexecutor/py_executor.py
  • tests/unittest/_torch/executor/test_kv_pool_rebalance.py

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #58329 [ run ] triggered by Bot. Commit: 236f52c Link to invocation

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #58329 [ run ] completed with state SUCCESS. Commit: 236f52c
/LLM/main/L0_MergeRequest_PR pipeline #46959 completed with status: 'FAILURE'

CI Report

⚠️ Action Required:

  • Please check the failed tests and fix your PR
  • If you cannot view the failures, ask the CI triggerer to share details
  • Once fixed, request an NVIDIA team member to trigger CI again

CI Agent Failure Analysis

Link to invocation

@thorjohnsen

Copy link
Copy Markdown
Collaborator Author

/bot

@github-actions

github-actions Bot commented Jul 9, 2026

Copy link
Copy Markdown

GitHub Bot Help

/bot [-h] ['run', 'kill', 'skip', 'reuse-pipeline'] ...

Provide a user friendly way for developers to interact with a Jenkins server.

Run /bot [-h|--help] to print this help message.

See details below for each supported subcommand.

Details

run [--reuse-test (optional)pipeline-id --disable-fail-fast --skip-test --stage-list "A10-PyTorch-1, xxx" --gpu-type "A30, H100_PCIe" --test-backend "pytorch, cpp" --add-multi-gpu-test --only-multi-gpu-test --disable-multi-gpu-test --post-merge --extra-stage "H100_PCIe-TensorRT-Post-Merge-1, xxx" --detailed-log --debug(experimental) --high-priority]

Launch build/test pipelines. All previously running jobs will be killed.

--reuse-test (optional)pipeline-id (OPTIONAL) : Allow the new pipeline to reuse build artifacts and skip successful test stages from a specified pipeline or the last pipeline if no pipeline-id is indicated. If the Git commit ID has changed, this option will be always ignored. The DEFAULT behavior of the bot is to reuse build artifacts and successful test results from the last pipeline.

--disable-reuse-test (OPTIONAL) : Explicitly prevent the pipeline from reusing build artifacts and skipping successful test stages from a previous pipeline. Ensure that all builds and tests are run regardless of previous successes.

--disable-fail-fast (OPTIONAL) : Disable fail fast on build/tests/infra failures.

--skip-test (OPTIONAL) : Skip all test stages, but still run build stages, package stages and sanity check stages. Note: Does NOT update GitHub check status.

--stage-list "A10-PyTorch-1, xxx" (OPTIONAL) : Only run the specified test stages. Supports wildcard * for pattern matching (e.g., "*PerfSanity*" matches all stages containing PerfSanity). Examples: "A10-PyTorch-1, xxx", "PerfSanity". Note: Does NOT update GitHub check status.

--gpu-type "A30, H100_PCIe" (OPTIONAL) : Only run the test stages on the specified GPU types. Examples: "A30, H100_PCIe". Note: Does NOT update GitHub check status.

--test-backend "pytorch, cpp" (OPTIONAL) : Skip test stages which don't match the specified backends. Only support [pytorch, cpp, tensorrt, triton]. Examples: "pytorch, cpp" (does not run test stages with tensorrt or triton backend). Note: Does NOT update GitHub pipeline status.

--only-multi-gpu-test (OPTIONAL) : Only run the multi-GPU tests. Note: Does NOT update GitHub check status.

--disable-multi-gpu-test (OPTIONAL) : Disable the multi-GPU tests. Note: Does NOT update GitHub check status.

--add-multi-gpu-test (OPTIONAL) : Force run the multi-GPU tests in addition to running L0 pre-merge pipeline.

--post-merge (OPTIONAL) : Run the L0 post-merge pipeline instead of the ordinary L0 pre-merge pipeline.

--extra-stage "H100_PCIe-TensorRT-Post-Merge-1, xxx" (OPTIONAL) : Run the ordinary L0 pre-merge pipeline and specified test stages. Supports wildcard * for pattern matching. Examples: --extra-stage "H100_PCIe-TensorRT-Post-Merge-1, xxx", --extra-stage "Post-Merge".

--detailed-log (OPTIONAL) : Enable flushing out all logs to the Jenkins console. This will significantly increase the log volume and may slow down the job.

--debug (OPTIONAL) : Experimental feature. Enable access to the CI container for debugging purpose. Note: Specify exactly one stage in the stage-list parameter to access the appropriate container environment. Note: Does NOT update GitHub check status.

--high-priority (OPTIONAL) : Run the pipeline with high priority. This option is restricted to authorized users only and will route the job to a high-priority queue.

kill

kill

Kill all running builds associated with pull request.

skip

skip --comment COMMENT

Skip testing for latest commit on pull request. --comment "Reason for skipping build/test" is required. IMPORTANT NOTE: This is dangerous since lack of user care and validation can cause top of tree to break.

reuse-pipeline

reuse-pipeline

Reuse a previous pipeline to validate current commit. This action will also kill all currently running builds associated with the pull request. IMPORTANT NOTE: This is dangerous since lack of user care and validation can cause top of tree to break.

@thorjohnsen

Copy link
Copy Markdown
Collaborator Author

/bot run

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #58504 [ run ] triggered by Bot. Commit: 236f52c Link to invocation

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #58504 [ run ] completed with state SUCCESS. Commit: 236f52c
/LLM/main/L0_MergeRequest_PR pipeline #47111 completed with status: 'FAILURE'

CI Report

⚠️ Action Required:

  • Please check the failed tests and fix your PR
  • If you cannot view the failures, ask the CI triggerer to share details
  • Once fixed, request an NVIDIA team member to trigger CI again

CI Agent Failure Analysis

Link to invocation

@thorjohnsen

Copy link
Copy Markdown
Collaborator Author

/bot run

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #58989 [ run ] triggered by Bot. Commit: 236f52c Link to invocation

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #58989 [ run ] completed with state SUCCESS. Commit: 236f52c
/LLM/main/L0_MergeRequest_PR pipeline #47519 completed with status: 'FAILURE'

CI Report

⚠️ Action Required:

  • Please check the failed tests and fix your PR
  • If you cannot view the failures, ask the CI triggerer to share details
  • Once fixed, request an NVIDIA team member to trigger CI again

CI Agent Failure Analysis

Link to invocation

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

Thanks for the PR!

runner = getattr(self.model_engine, "cuda_graph_runner", None)
if runner is not None and runner.padding_dummy_requests:
for dummy in runner.padding_dummy_requests.values():
mgr.free_resources(dummy)

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.

This only releases the primary KV cache, shall we also clear the other places? For example, CUDAGraphRunner can also register the same dummy ID with other places, e.g., one-model draft KV manager, speculative resource manager, and encoder-decoder cross-KV manager.

I am also wondering if there is a way that we can centralize the cleanup.

@yizhang-nv

Copy link
Copy Markdown
Member

Should address the other manager's free resource as well.

# living cache to be suspended. Close them and let _pad_batch
# recreate them on the next padded batch, with the post-adjust
# pool layout.
runner = getattr(self.model_engine, "cuda_graph_runner", None)

@liji-nv liji-nv Jul 23, 2026

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.

Does the functionality conflict with #16072

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.

CC @kaiyux

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

I think it conflicts with #16072. We will have to figure out a way to combine these two.

@thorjohnsen

Copy link
Copy Markdown
Collaborator Author

/bot run --disable-fail-fast

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #61357 [ run ] triggered by Bot. Commit: 82b8dee Link to invocation

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #61357 [ run ] completed with state FAILURE. Commit: 82b8dee
/LLM/main/L0_MergeRequest_PR pipeline #49586 completed with status: 'FAILURE'

CI Report

⚠️ Action Required:

  • Please check the failed tests and fix your PR
  • If you cannot view the failures, ask the CI triggerer to share details
  • Once fixed, request an NVIDIA team member to trigger CI again

CI Agent Failure Analysis

Link to invocation

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants