Skip to content

[TRTLLM-14903][fix] Free partially-allocated warmup dummy KV blocks and count spec extra tokens in warmup block estimates - #17162

Merged
brnguyen2 merged 8 commits into
NVIDIA:mainfrom
brnguyen2:fix/TRTLLM-14903-estimation-warmup-leak
Aug 5, 2026
Merged

[TRTLLM-14903][fix] Free partially-allocated warmup dummy KV blocks and count spec extra tokens in warmup block estimates#17162
brnguyen2 merged 8 commits into
NVIDIA:mainfrom
brnguyen2:fix/TRTLLM-14903-estimation-warmup-leak

Conversation

@brnguyen2

@brnguyen2 brnguyen2 commented Aug 1, 2026

Copy link
Copy Markdown
Collaborator

Description

Fixes TRTLLM-14903: LLM() startup hangs indefinitely during KV cache size estimation for Mamba-hybrid models with speculative decoding enabled.

Two defects combined to produce the hang:

  1. _create_warmup_request under-counted blocks_to_use: it ignored the per-sequence extra tokens (num_extra_kv_tokens, num_extra_decoding_steps, and the draft-token reserve for generation dummies) that add_dummy_requests actually allocates. With spec decoding, block-aligned multi-sequence warmup shapes (e.g. the Mamba hybrid multi-seq warmup added in [None][perf] Close Mamba hybrid warmup gap in autotuner warmup #16177) passed the estimate but overflowed the pool at allocation time.
  2. add_dummy_requests leaked every already-registered sequence when a later add_token raised (e.g. "no free blocks left"). On the minimal KV pool built for cache-size estimation, the leak left too few blocks for the estimation requests themselves, so the executor loop spun forever without scheduling them and LLM() never returned.

The fix makes blocks_to_use mirror the real allocation, and makes add_dummy_requests remove already-registered sequences before re-raising, preserving callers' skip-on-failure semantics.

Note: feat/kimi_k3 currently carries a temporary workaround for this issue (estimation skipped whenever a speculative config is set, in py_executor_creator.py, marked TRTLLM-14903); that workaround should be reverted once this fix merges.

Test Coverage

Verified on a spec-decoding estimation-phase integration run on a Mamba-hybrid model: previously hung unboundedly during estimation warmup; with the fix the run completes and passes logits parity against a non-speculative baseline. The "Mamba hybrid warmup skipped" overflow path is now taken before any allocation, so the pool is no longer poisoned.

PR Checklist

  • PR title and description follow the repo conventions
  • Test coverage noted above

Dev Engineer Review

  • _create_warmup_request now accounts for extra KV tokens, decoding steps, draft-loop reservations, and beam width.
  • add_dummy_requests removes partially registered target and draft sequences after allocation failure.
  • Cleanup attempts all target and draft requests and re-raises the first cleanup failure.
  • The change prevents leaked KV blocks during KV-cache estimation.
  • The V2 KV-cache manager already releases partial dummy allocations.
  • The benchmark harness removes PYTHONSAFEPATH before launching the client.
  • No public API, configuration, or test-list changes were identified.
  • Mamba-hybrid speculative-decoding validation showed matching logits parity.
  • The 16-GPU GSM8K run scored 96.66, matching the reference run.
  • One CI run was disabled for instance maintenance. Other reported failures were attributed to infrastructure or environment issues, and targeted reruns passed.

QA Engineer Review

  • The test code in tests/integration/defs/kv_cache/test_prefix_aware_scheduling.py was modified.
  • No test functions were added, modified, or removed.
  • Existing integration coverage remains applicable.
  • Verdict: sufficient.

@brnguyen2

Copy link
Copy Markdown
Collaborator Author

/bot run

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #63215 [ run ] triggered by Bot. Commit: e6b4c3d Link to invocation

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #63215 [ run ] completed with state DISABLED
Pipeline is freezed and top-1 instance is under maintenance. For urgent request, contact Yiteng Niu

Link to invocation

@brnguyen2

Copy link
Copy Markdown
Collaborator Author

Validated beyond the original truncated-model repro: on a Mamba-hybrid MoE model with suffix-automaton speculative decoding and KV-cache estimation enabled (the previously hanging configuration), a full speculative-decoding logits-parity integration run now completes in ~17 minutes with parity statistics identical to the estimation-skipped and pre-regression baselines (52 prompts, 0 drift), with the estimation phase confirmed active in the logs and no warmup-overflow warnings. A GSM8K accuracy run on the full model at 16-GPU scale with speculative decoding and estimation enabled scores 96.66, matching the reference measured with estimation skipped.

An audit of the V2 KV-cache manager's add_dummy_requests found it already frees partially-allocated dummy requests on capacity failure (success-flag based signaling with an explicit release_resources() on every capacity-failure path), so it does not need the equivalent change.

Marking the PR ready for review. The temporary estimation-skip workaround on feat/kimi_k3 has been reverted on that branch now that the fix is validated there.

@brnguyen2
brnguyen2 marked this pull request as ready for review August 1, 2026 16:38
@brnguyen2
brnguyen2 requested review from a team as code owners August 1, 2026 16:38
@coderabbitai

coderabbitai Bot commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

The change updates warmup KV-cache capacity estimation, makes dummy-request setup transactional, and removes PYTHONSAFEPATH before launching LMBenchmark.

Changes

KV-cache management

Layer / File(s) Summary
Warmup capacity estimation
tensorrt_llm/_torch/pyexecutor/model_engine.py
Warmup estimation now includes extra KV and decode tokens, draft-loop reservations, and beam-width multiplication.
Dummy-request rollback
tensorrt_llm/_torch/pyexecutor/resource_manager.py
If dummy target or draft sequence setup fails, registered sequences are removed before the exception is re-raised. Cleanup errors take precedence.
Benchmark subprocess setup
tests/integration/defs/kv_cache/test_prefix_aware_scheduling.py
The LMBenchmark subprocess removes PYTHONSAFEPATH before launch.

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

Possibly related PRs

Suggested reviewers: bowenfu, chuangz0, allisonlim-nv

🚥 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 follows the repository format and clearly identifies the fix for partial KV-block cleanup and speculative-decoding warmup estimates.
Description check ✅ Passed The description includes the required Description, Test Coverage, and PR Checklist sections and explains the issue, solution, and validation.
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 (2)
tensorrt_llm/_torch/pyexecutor/resource_manager.py (2)

976-980: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Unbind the unused loop variable.

token_num is not read in this loop. Ruff reports B007.

♻️ Proposed fix
-                for req_id, token_num, _ in batch_request_infos:
+                for req_id, _, _ in batch_request_infos:
🤖 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 `@tensorrt_llm/_torch/pyexecutor/resource_manager.py` around lines 976 - 980,
Update the loop over batch_request_infos to bind the unused token_num element to
an underscore, preserving req_id and the existing token-addition loops.

Source: Linters/SAST tools


1039-1046: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Narrow the swallowed exception and log it.

The inner handler discards every exception type. A real failure in remove_sequence (for example a binding signature change) then disappears, and the rollback silently stops for that request. The C++ binding raises RuntimeError for an unregistered sequence, so catch that type and log at debug level. Ruff reports S110 and BLE001 here.

♻️ Proposed fix
                 for req in freeing_requests:
                     try:
                         freeing_impl.remove_sequence(req.py_request_id, req,
                                                      False)
-                    except Exception:
+                    except RuntimeError as e:
                         # The sequence may never have been registered (the
                         # batched add itself failed); nothing to clean up.
-                        pass
+                        logger.debug(
+                            "Dummy request rollback skipped for request "
+                            f"{req.py_request_id}: {e}")

As per coding guidelines "Catch the narrowest possible exceptions, keep duck-typing try blocks minimal, prefer isinstance(), use built-in exception types".

🤖 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 `@tensorrt_llm/_torch/pyexecutor/resource_manager.py` around lines 1039 - 1046,
Update the exception handler in the freeing_requests rollback loop around
freeing_impl.remove_sequence to catch only RuntimeError, log the caught
exception at debug level, and allow other exception types to propagate. Keep the
try block limited to the remove_sequence call so genuine rollback failures are
not silently swallowed and Ruff S110/BLE001 are resolved.

Sources: Coding guidelines, Linters/SAST tools

🤖 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 `@tensorrt_llm/_torch/pyexecutor/resource_manager.py`:
- Around line 976-980: Update the loop over batch_request_infos to bind the
unused token_num element to an underscore, preserving req_id and the existing
token-addition loops.
- Around line 1039-1046: Update the exception handler in the freeing_requests
rollback loop around freeing_impl.remove_sequence to catch only RuntimeError,
log the caught exception at debug level, and allow other exception types to
propagate. Keep the try block limited to the remove_sequence call so genuine
rollback failures are not silently swallowed and Ruff S110/BLE001 are resolved.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 43777f60-714f-419b-8a39-2e0d0fcfe29b

📥 Commits

Reviewing files that changed from the base of the PR and between fdf7bd5 and e6b4c3d.

📒 Files selected for processing (2)
  • tensorrt_llm/_torch/pyexecutor/model_engine.py
  • tensorrt_llm/_torch/pyexecutor/resource_manager.py

@brnguyen2

Copy link
Copy Markdown
Collaborator Author

/bot run

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #63288 [ run ] triggered by Bot. Commit: e6b4c3d Link to invocation

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #63288 [ run ] completed with state FAILURE. Commit: e6b4c3d
/LLM/main/L0_MergeRequest_PR pipeline #51283 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

@brnguyen2

Copy link
Copy Markdown
Collaborator Author

The test_multi_round_qa_shared_prefix_smoke failure in the last pre-merge run is unrelated to this PR: the LMBenchmark client dies at import time with ModuleNotFoundError: No module named 'utils' because the test environment on the affected runners now sets PYTHONSAFEPATH=1, which disables the script-directory sys.path entry that multi-round-qa.py relies on to import its sibling utils.py. The same failure appears on many concurrent PRs' pre-merge runs since 2026-08-02 (e.g. #17163, #17165, #16957, #16609, #16993, #16592).

Folded a harness fix into this PR (dc5a809): strip PYTHONSAFEPATH from the benchmark client's subprocess environment, restoring the standard CPython script-directory import behavior the script depends on.

@brnguyen2

Copy link
Copy Markdown
Collaborator Author

/bot run

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #64015 [ run ] triggered by Bot. Commit: 45e7638 Link to invocation

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #64010 [ run ] completed with state ABORTED. Commit: 9320c32

Link to invocation

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #64015 [ run ] completed with state SUCCESS. Commit: 45e7638
/LLM/main/L0_MergeRequest_PR pipeline #51947 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

…nd count spec extra tokens in warmup block estimates

Two defects combined to hang LLM startup indefinitely during KV cache
size estimation for Mamba-hybrid models with speculative decoding:

1. _create_warmup_request under-counted blocks_to_use: it ignored the
   per-sequence extra tokens (num_extra_kv_tokens,
   num_extra_decoding_steps, and the draft-token reserve for generation
   dummies) that add_dummy_requests actually allocates. With spec
   decoding, block-aligned multi-sequence warmup shapes (e.g. the Mamba
   hybrid multi-seq warmup) passed the estimate but overflowed the pool
   at allocation time.

2. add_dummy_requests leaked every already-registered sequence when a
   later add_token raised (e.g. "no free blocks left"). On the minimal
   KV pool built for cache-size estimation the leak left too few blocks
   for the estimation requests themselves, so the executor loop spun
   forever without scheduling them and LLM() never returned.

Fix blocks_to_use to mirror the real allocation, and make
add_dummy_requests remove already-registered sequences before
re-raising, preserving callers' skip-on-failure semantics.

Verified on a spec-decoding estimation-phase integration run on a
Mamba-hybrid model (previously hung unboundedly; now completes with
logits parity against a non-speculative baseline).

Signed-off-by: Brian Nguyen <brnguyen@nvidia.com>
multi-round-qa.py imports its sibling utils.py through the implicit
script-directory sys.path entry. When the test environment sets
PYTHONSAFEPATH=1, that entry is disabled and the benchmark client exits
immediately with ModuleNotFoundError: No module named 'utils', failing
TestServePrefixAwareScheduling tests with 'Smoke warmup failed with
rc=1' while the server is still healthy. Strip the variable from the
client subprocess environment.

Signed-off-by: Brian Nguyen <brnguyen@nvidia.com>
Signed-off-by: Brian Nguyen <brnguyen@nvidia.com>
Signed-off-by: Brian Nguyen <brnguyen@nvidia.com>
…ailures

remove_sequence is already a no-op for request ids the failed batched
add never registered, so the per-request exception handler only masked
real releaseBlocks failures — which leave the KV pool poisoned, the
same hang mechanism this cleanup exists to prevent. Attempt cleanup
for every target and draft request, then re-raise the first cleanup
failure instead of swallowing it.

Signed-off-by: Brian Nguyen <brnguyen@nvidia.com>
A 4-block pool admits both dummy sequences but runs out of blocks in
the per-request draft add_token loop, exercising the partial-failure
path: the exception must propagate and every already-allocated block
must be freed. Fails against the pre-fix code (blocks leak), passes
with the cleanup.

Signed-off-by: Brian Nguyen <brnguyen@nvidia.com>
@brnguyen2

Copy link
Copy Markdown
Collaborator Author

/bot run

@brnguyen2
brnguyen2 force-pushed the fix/TRTLLM-14903-estimation-warmup-leak branch from 45e7638 to 01196c0 Compare August 5, 2026 12:03
@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #64044 [ run ] triggered by Bot. Commit: 01196c0 Link to invocation

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #64044 [ run ] completed with state SUCCESS. Commit: 01196c0
/LLM/main/L0_MergeRequest_PR pipeline #51974 completed with status: 'FAILURE'

CI Report

⚠️ Multi-GPU Label Required:
Multi-GPU tests require the ci: full pre-merge approved label on this PR. Ask a member of NVIDIA/trt-llm-ci-approvers to add the label, then re-trigger CI with the same bot command (no rebase needed).

⚠️ 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

unittest/auto_deploy/singlegpu/custom_ops/moe/test_trtllm_moe.py and
unittest/auto_deploy/singlegpu/transformations/library/test_moe_fusion.py
fail in DGX_B200-AutoDeploy-1 for any PR rebased onto current main:
identical failures on pipelines 51974 (this PR) and 51977 (PR NVIDIA#17225,
zero file overlap). Suspect commit 89bba4c (NVIDIA#15297), which modifies
the trtllm_moe custom op and Blackwell blockScaleMoe kernels. Waived
pending an NVBug; the entries will be updated with the bug link once
filed.

Signed-off-by: Brian Nguyen <brnguyen@nvidia.com>
@brnguyen2
brnguyen2 requested a review from a team as a code owner August 5, 2026 15:52
@brnguyen2

Copy link
Copy Markdown
Collaborator Author

/bot run --stage-list "DGX_B200-AutoDeploy-1"

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #64078 [ run ] triggered by Bot. Commit: 1fa12af Link to invocation

Cites nvbugs/6564714 for the two waives added in the previous commit.

Signed-off-by: Brian Nguyen <brnguyen@nvidia.com>
@brnguyen2

Copy link
Copy Markdown
Collaborator Author

/bot run --stage-list "DGX_B200-AutoDeploy-1"

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #64087 [ run ] triggered by Bot. Commit: c4f06e9 Link to invocation

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github/17162-1fa12af #64078 was force-killed by a newer pipeline run.
L0 job information not available (job may not have been triggered yet).

Link to superseding invocation

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #64087 [ run ] completed with state SUCCESS. Commit: c4f06e9
/LLM/main/L0_MergeRequest_PR pipeline #52014 (Partly Tested) completed with status: 'SUCCESS'

CI Report

Link to invocation

@brnguyen2

Copy link
Copy Markdown
Collaborator Author

/bot skip --comment "Waive-only change on top of fully qualified head; targeted DGX_B200-AutoDeploy-1 run on this exact commit passed (pipeline 52014); waived tests are a pre-existing main-side breakage tracked in nvbugs/6564714."

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #64107 [ skip ] triggered by Bot. Commit: c4f06e9 Link to invocation

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #64107 [ skip ] completed with state SUCCESS. Commit: c4f06e9
Skipping testing for commit c4f06e9

Link to invocation

@brnguyen2
brnguyen2 merged commit a23e8de into NVIDIA:main Aug 5, 2026
7 checks passed
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.

7 participants