Skip to content

[TRTLLM-14054][perf] Reduce per-step host preparation overhead in the PyTorch executor decode path#16313

Merged
kaiyux merged 5 commits into
NVIDIA:mainfrom
kaiyux:perf/decode-step-host-prep-20260713
Jul 17, 2026
Merged

[TRTLLM-14054][perf] Reduce per-step host preparation overhead in the PyTorch executor decode path#16313
kaiyux merged 5 commits into
NVIDIA:mainfrom
kaiyux:perf/decode-step-host-prep-20260713

Conversation

@kaiyux

@kaiyux kaiyux commented Jul 13, 2026

Copy link
Copy Markdown
Member

Description

At high concurrency, the per-step host preparation work in the PyTorch executor's overlap decode loop is exposed on the critical path: on Qwen3.6-35B-A3B-NVFP4 (hybrid gated-delta-net + MoE) at concurrency 256, nsys shows a recurring GPU-idle gap between decode steps dominated by _prepare_inputs. This PR removes three sources of that overhead:

  • Bound the KV-cache block-table staging to the batch's real width. KVCacheManager.copy_batch_block_offsets staged block offsets through a freshly allocated pinned buffer sized for max_seq_len worth of block columns — ~33.5 MB of pinned allocation + host memcpy + H2D copy per step at batch 256 with max_seq_len=262144, of which only ~0.9 MB was live. A new optional max_blocks argument bounds the staged/H2D width by the batch's maximum KV length; kernels only read each sequence's allocated block prefix, and the persistent host buffer (used by synchronous CPU readers) is untouched. Default behavior is unchanged, and KVCacheManagerV2 accepts the argument for signature parity (its copy already scales with allocated blocks only).

  • Stop re-walking unchanged generation batches in _prepare_tp_inputs. In non-speculative overlap decode, when the scheduled batch is exactly the same set of generation requests as the previous step, every request advanced by exactly one committed token. A full prepare pass now records the batch-invariant state (request ids, prompt lens, seq-lens, pinned position buffer) when the batch qualifies — generation-only, non-dummy, all requests carrying a previous-step device tensor, no spec decode / beam search / LoRA / multimodal / mrope work / attention-DP — and subsequent steps advance positions with one vectorized increment, gather input tokens from the previous step's device sample buffer, and refresh only per-step metadata. Any full pass invalidates and re-records the cache.

  • Vectorize mamba cache-manager bookkeeping. Replace per-request .item() reads and per-element pinned-buffer stores with one bulk tolist() / copy_() per step.

nsys at concurrency 256: _prepare_inputs drops from 2.999 ms to 0.646 ms per decode step, and per-step GPU idle from 8.6% to 2.5%.

Test Coverage

  • The touched prepare / block-offset staging paths are exercised by the existing _torch executor and attention-backend suites; the steady-state fast path engages for any non-speculative overlap-scheduler decode workload and is invalidated by any batch-composition change.
  • Perf: trtllm-serve + benchmark_serving on Qwen3.6-35B-A3B-NVFP4, 1x B200, ISL 1024 / OSL 6144, random dataset with --ignore-eos, one point per concurrency (paired num_prompts 8...1024), measured at base 8ef7190ebf:
concurrency baseline (tok/s) this PR (tok/s) delta
1 329.09 345.15 +4.88%
2 615.93 638.76 +3.71%
4 1134.41 1166.02 +2.79%
8 2048.03 2080.29 +1.58%
16 3601.60 3711.45 +3.05%
32 6095.07 6256.17 +2.64%
64 9826.85 10079.27 +2.57%
128 14745.82 15270.86 +3.56%
256 19940.84 20685.34 +3.73%

Output throughput improves +3.17% on average across the 9-point curve, positive at every point. Independent of #16314; on the same harness the two compose to about +5.6%.

Update (2026-07-13): hardening fixes + steady-state nsys A/B

The branch now carries a follow-up commit (12ceb00216) hardening the fast prepare path from review findings:

  • DeepseekV4CacheManager.copy_batch_block_offsets accepts max_blocks for signature parity (the base prepare() now always passes it).
  • Fast steps advance the device position buffer in place instead of staging a mutated pinned buffer through an async H2D whose previous-step copy may still be pending under the overlap scheduler (the nvbug 6293536 hazard class); this also removes one transfer per step.
  • MRoPE models keep the (3,1,N) position layout on the fast path: text-only batches advance mrope_position_ids_cuda in place and return the mrope view, matching the full pass in eager, piecewise, and CUDA-graph modes (Qwen3.5/3.6 normalize rope_parameters with mrope_section into rope_scaling.type='mrope', so they take this path even for pure-text serving).
  • Steady-state recording snapshots num_cached_tokens_per_seq before attn_metadata.prepare(), so backends that clamp the aliased list in place (RocketKV) re-clamp from true values every step instead of compounding into the recorded state.
  • The incremental-update early return also invalidates the steady-state cache, so the two fast paths can never interleave.

Fresh nsys A/B at the head of this branch vs its merge base 53d2bde62f — same GPU, same prebuilt binaries (python-only diff), Qwen3.6-35B-A3B-NVFP4 on 1x B200, trtllm-serve (CUDA graphs max_batch_size: 256 + padding, TRTLLM MoE backend) + benchmark_serving random ISL 1024 / OSL 6144 with --ignore-eos, c=256 / num_prompts=1024, capture window TLLM_PROFILE_START_STOP=100-150 (-t cuda,nvtx,python-gil -c cudaProfilerApi --cuda-graph-trace node). Both captures contain exactly 50 forward steps, every one 0 ctx reqs, 0 ctx tokens, 256 gen reqs — pure steady-state decode at the full batch:

metric (50-step window) baseline this PR delta
wall-clock per decode step 11.98 ms 10.20 ms -14.8%
GPU idle in window 11.49 % 2.23 % -80.5%
_prepare_inputs host time / step 4.018 ms 0.655 ms -83.7%
steady-state fast-path engagements 0 50 / 50
H2D traffic in window 1.68 GB 8.46 MB -99.5%

The H2D reduction is the bounded block-table staging; the prepare-time and idle reduction is the steady-state fast prepare, which engaged on all 50 steps — on an mrope-normalized model, so the window also exercises the mrope in-place advance under CUDA-graph replay. Correctness probe: a fixed greedy completion (temperature 0, 64 new tokens, fresh server per side) is byte-identical between the two sides.

Update (2026-07-14): CI fix — full-width block-table staging for speculative decoding

Pre-merge CI caught an illegal memory access during EAGLE3 warmup (test_llama_eagle3_dynamic_tree[True-False] on B200, test_eagle3_cdl_sampling[True] on H100, plus follow-on thread-leak failures in test_eagle3_cuda_graph_padding). Root cause: the batch-max cap on the staged block-table width assumed the host kv_lens snapshot bounds every block-table access. That holds only for the non-speculative path — speculative decoding advances kv_lens_cuda on device past the host snapshot (one-model draft/tree sub-steps, overlap-scheduler kv-len offsets), and draft-token blocks are allocated ahead of the host kv_lens, so spec kernels dereferenced block columns the cap left unstaged (uninitialized memory in the device buffer).

The new commit keeps the cap only when no speculative-decoding state is attached to the metadata (no draft KV cache manager, spec-dec attention mode off, no extra KV tokens, no draft engine or drafter in the runtime); speculative setups stage the full pre-allocated width exactly as before this PR. The non-speculative serving path that motivated the optimization keeps the bounded staging and its measured wins.

Verified on 1x B200 against LLM_MODELS_ROOT checkpoints: test_llama_eagle3_dynamic_tree[True-False] fails at the parent commit and passes with the fix; test_eagle3_cdl_sampling, test_eagle3_cuda_graph_padding, test_llama_eagle3[True-TRTLLM-False-True-True-False-True-False-False-False] (spec-vs-ref equality with the overlap-scheduler ref exercising the steady-state fast prepare), and test_kv_block_offset_overlap_race.py all pass. New unit tests pin the staging-width gate (test_block_offsets_staging_width_spec_gate) and the max_blocks copy semantics (test_copy_batch_block_offsets_max_blocks_staging_width).

PR Checklist

  • PR description clearly explains what and why. If using CodeRabbit's summary, please make sure it makes sense.

  • PR Follows TRT-LLM CODING GUIDELINES to the best of your knowledge.

  • Test cases are provided for new code paths (see test instructions)

  • If PR introduces API changes, an appropriate PR label is added - either api-compatible or api-breaking. For api-breaking, include BREAKING in the PR title.

  • Any new dependencies have been scanned for license and vulnerabilities

  • CODEOWNERS updated if ownership changes

  • Documentation updated as needed

  • Update tava architecture diagram if there is a significant design change in PR.

  • The reviewers assigned automatically/manually are appropriate for the PR.

  • Please check this after reviewing the above items as appropriate for this PR.

GitHub Bot Help

To see a list of available CI bot commands, please comment /bot help.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Performance Improvements

    • Improved generation-step preparation for eligible workloads, reducing repeated input-processing overhead.
    • Reduced KV-cache block-table staging to only the portion required by each batch.
    • Optimized state-index transfers for faster cache preparation.
  • Bug Fixes

    • Improved validation and handling of KV-cache sequence lengths and state mappings.
    • Ensured cache metadata remains accurate when processing changing request batches.

@kaiyux
kaiyux requested review from a team as code owners July 13, 2026 08:42
@kaiyux kaiyux changed the title [None][perf] Reduce per-step host preparation overhead in the PyTorch executor decode path [TRTLLM-14054][perf] Reduce per-step host preparation overhead in the PyTorch executor decode path Jul 13, 2026
@coderabbitai

coderabbitai Bot commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Changes

The PR adds a steady-state generation fast path, bounds KV-cache block-offset staging to the batch’s required width, and replaces per-element Mamba state-index processing with bulk conversions and active-request mappings.

Runtime preparation updates

Layer / File(s) Summary
Steady generation fast preparation
tensorrt_llm/_torch/pyexecutor/model_engine.py
PyTorchModelEngine caches qualifying generation batches, reuses preallocated buffers, advances positions, and refreshes per-step attention metadata.
Bounded KV block-offset staging
tensorrt_llm/_torch/attention_backend/trtllm.py, tensorrt_llm/_torch/pyexecutor/resource_manager.py, tensorrt_llm/_torch/pyexecutor/kv_cache_manager_v2.py
Attention preparation computes a batch-specific block limit, and KV-cache managers accept and apply the bounded staging width.
Mamba state-index preparation
tensorrt_llm/_torch/modules/mamba/mamba2_metadata.py, tensorrt_llm/_torch/pyexecutor/mamba_cache_manager.py
Mamba state indices are copied in bulk, while request mappings and dummy masks use the active request collection.

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

Sequence Diagram(s)

sequenceDiagram
  participant PyTorchModelEngine
  participant steady_gen_cache
  participant attention_metadata
  participant device_input_buffers
  PyTorchModelEngine->>steady_gen_cache: check generation eligibility
  PyTorchModelEngine->>attention_metadata: refresh per-step metadata
  PyTorchModelEngine->>device_input_buffers: copy next input tokens
Loading

Suggested reviewers: qijune, shixiaowei02, arysef, suyoggupta

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 25.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
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 is concise, specific, and matches the main performance optimization in the PyTorch executor decode path.
Description check ✅ Passed The description includes the problem, solution, test coverage, and checklist, and is mostly complete for the template.
✨ 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.

Actionable comments posted: 1

🧹 Nitpick comments (1)
tensorrt_llm/_torch/pyexecutor/resource_manager.py (1)

2151-2242: 🚀 Performance & Scalability | 🔵 Trivial | 💤 Low value

CROSS + beam_width > 1 branch doesn't forward max_blocks.

_stage_block_offsets_for_copy(num_rows) in the cross-attention branch (line 2177) omits max_blocks, so it always stages the full max_blocks_per_seq width even though the caller in trtllm.py now computes and passes a tighter cap. This isn't a correctness issue (same "read-only-allocated-prefix" invariant still holds) but leaves the cross-attention/beam-width>1 combination without the intended H2D footprint reduction.

♻️ Proposed fix
             num_rows = num_context + num_gen_requests
-            host_block_offsets = self._stage_block_offsets_for_copy(num_rows)
+            host_block_offsets = self._stage_block_offsets_for_copy(
+                num_rows, max_blocks)
🤖 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 2151 - 2242,
Update the CROSS and beam_width > 1 path in copy_batch_block_offsets to pass the
caller-provided max_blocks value into _stage_block_offsets_for_copy, matching
the non-cross path. Preserve the existing row expansion and asynchronous copy
behavior while ensuring staging uses the tighter block-width cap.
🤖 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 `@tensorrt_llm/_torch/pyexecutor/mamba_cache_manager.py`:
- Around line 1971-1974: Update the `_setup_state_indices` dummy-mask refresh to
use only the current `requests` batch; remove the fallback that rebuilds the
mask from `self.requests`. Preserve the existing `_refresh_dummy_request_mask`
behavior and rely on `get_state_indices()` for reordered batches.

---

Nitpick comments:
In `@tensorrt_llm/_torch/pyexecutor/resource_manager.py`:
- Around line 2151-2242: Update the CROSS and beam_width > 1 path in
copy_batch_block_offsets to pass the caller-provided max_blocks value into
_stage_block_offsets_for_copy, matching the non-cross path. Preserve the
existing row expansion and asynchronous copy behavior while ensuring staging
uses the tighter block-width cap.
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: fc111ef9-2173-4dde-b0c7-4c08d1d97b24

📥 Commits

Reviewing files that changed from the base of the PR and between 53d2bde and c9c330c.

📒 Files selected for processing (6)
  • tensorrt_llm/_torch/attention_backend/trtllm.py
  • tensorrt_llm/_torch/modules/mamba/mamba2_metadata.py
  • tensorrt_llm/_torch/pyexecutor/kv_cache_manager_v2.py
  • tensorrt_llm/_torch/pyexecutor/mamba_cache_manager.py
  • tensorrt_llm/_torch/pyexecutor/model_engine.py
  • tensorrt_llm/_torch/pyexecutor/resource_manager.py

Comment thread tensorrt_llm/_torch/pyexecutor/mamba_cache_manager.py
@kaiyux

kaiyux commented Jul 13, 2026

Copy link
Copy Markdown
Member Author

/bot run --disable-fail-fast

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #58981 [ run ] triggered by Bot. Commit: 12ceb00 Link to invocation

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #58981 [ run ] completed with state SUCCESS. Commit: 12ceb00
/LLM/main/L0_MergeRequest_PR pipeline #47511 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

@kaiyux

kaiyux commented Jul 14, 2026

Copy link
Copy Markdown
Member Author

/bot run --disable-fail-fast

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #59066 [ run ] triggered by Bot. Commit: 12ceb00 Link to invocation

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #59066 [ run ] completed with state SUCCESS. Commit: 12ceb00
/LLM/main/L0_MergeRequest_PR pipeline #47589 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

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

Attention modification LGTM

Comment thread tensorrt_llm/_torch/attention_backend/trtllm.py Outdated
@kaiyux

kaiyux commented Jul 14, 2026

Copy link
Copy Markdown
Member Author

/bot run

@kaiyux
kaiyux force-pushed the perf/decode-step-host-prep-20260713 branch from 9aad9cb to 78c363c Compare July 16, 2026 05:10
@kaiyux

kaiyux commented Jul 16, 2026

Copy link
Copy Markdown
Member Author

/bot run --disable-fail-fast

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #59622 [ run ] triggered by Bot. Commit: 78c363c Link to invocation

@nvpohanh
nvpohanh requested a review from VALLIS-NERIA July 16, 2026 07:24
@nvpohanh

Copy link
Copy Markdown
Collaborator

[by Codex] @VALLIS-NERIA Friendly reminder to review this PR as the primary KV-cache-manager reviewer when you have a chance. Thanks!

@yizhang-nv yizhang-nv left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

LGTM from KVCM side

Comment thread tensorrt_llm/_torch/modules/mamba/mamba2_metadata.py
@kaiyux

kaiyux commented Jul 16, 2026

Copy link
Copy Markdown
Member Author

/bot help

@github-actions

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.

@kaiyux

kaiyux commented Jul 16, 2026

Copy link
Copy Markdown
Member Author

/bot kill

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #59680 [ kill ] triggered by Bot. Commit: 78c363c Link to invocation

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #59622 [ run ] completed with state ABORTED. Commit: 78c363c

Link to invocation

kaiyux added a commit to kaiyux/TensorRT-LLM that referenced this pull request Jul 16, 2026
The bulk copy_ raises on most length mismatches, but a length-1
sequence would broadcast silently. Assert the one-index-per-request
contract explicitly so out-of-tree cache managers fail loudly.

Addresses review feedback on NVIDIA#16313.

Signed-off-by: Kaiyu Xie <26294424+kaiyux@users.noreply.github.com>
@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #59680 [ kill ] completed with state SUCCESS. Commit: 78c363c
Successfully killed previous jobs for commit 78c363c

Link to invocation

@kaiyux

kaiyux commented Jul 16, 2026

Copy link
Copy Markdown
Member Author

Need to rebase and relaunch CI pipeline after #16444 gets merged.

kaiyux added 5 commits July 16, 2026 20:13
… executor decode path

At high concurrency, per-step host preparation in the overlap decode
loop is exposed on the critical path. This change removes three sources
of that overhead:

- KVCacheManager.copy_batch_block_offsets staged block offsets through a
  freshly allocated pinned buffer sized for max_seq_len worth of block
  columns (~33.5 MB per step at batch size 256 with max_seq_len=262144),
  while the batch actually needed ~0.9 MB. Bound the staged/H2D copy
  width by the batch's maximum KV length (new optional max_blocks
  argument); kernels only read each sequence's allocated block prefix.

- _prepare_tp_inputs re-walked the whole batch in Python every step even
  when the scheduled generation batch was unchanged. Cache the
  batch-composition-invariant state for non-speculative overlap decode
  (steady-state generation-only batches) and advance positions with one
  vectorized op, refreshing only per-step metadata. Any full prepare
  pass invalidates and re-records the cache.

- Mamba cache-manager bookkeeping did one small tensor read or write per
  request (.item() calls and per-element stores). Replace them with one
  bulk tolist()/copy_ per step.

Measured with trtllm-serve + benchmark_serving on Qwen3.6-35B-A3B-NVFP4
(hybrid gated-delta-net + MoE) on 1x B200, ISL=1024/OSL=6144, over a
concurrency sweep 1..256: output throughput improves 3.17% on average
across the 9-point curve (+1.58% to +4.88%, positive at every point).
nsys at concurrency 256 shows _prepare_inputs going from 2.999 ms to
0.646 ms per decode step and per-step GPU idle from 8.6% to 2.5%.

Signed-off-by: Kaiyu Xie <26294424+kaiyux@users.noreply.github.com>
- Accept max_blocks in DeepseekV4CacheManager.copy_batch_block_offsets for
  signature parity with the base managers; TrtllmAttentionMetadata.prepare
  now always passes it.
- Advance the device position buffer in place on fast steps instead of
  staging a mutated pinned buffer through an async H2D whose previous-step
  copy may still be pending under the overlap scheduler (the nvbug 6293536
  hazard class); the pinned counter is host-side bookkeeping only.
- Keep the (3,1,N) mrope position layout on the fast path: text-only batches
  on mrope models advance mrope_position_ids_cuda in place and return the
  mrope view, matching the full pass in eager, piecewise, and CUDA-graph
  modes.
- Record the steady-gen cached-token counts from a pre-prepare snapshot so
  in-place clamping by sparse backends (RocketKV) is re-applied from true
  values each step instead of compounding into the recorded state.
- Invalidate the steady-gen cache on the incremental-update early return so
  the two fast paths can never interleave.

Signed-off-by: Kaiyu Xie <26294424+kaiyux@users.noreply.github.com>
…ve decoding

The batch-max cap on the staged block-table width assumed the host kv_lens
snapshot bounds every block-table access. That holds only for the
non-speculative path: speculative decoding advances kv_lens_cuda on device
past the host snapshot (one-model draft/tree sub-steps, overlap-scheduler
kv-len offsets), and draft-token blocks are allocated ahead of the host
kv_lens, so its kernels dereference block columns the cap left unstaged --
uninitialized values in the device buffer. CI hit this as an illegal memory
access during EAGLE3 warmup (test_llama_eagle3_dynamic_tree on B200,
test_eagle3_cdl_sampling on H100).

Keep the cap only when no speculative-decoding state is attached to the
metadata (no draft KV cache manager, spec-dec attention mode off, no extra
KV tokens, no draft engine or drafter in the runtime); speculative setups
stage the full pre-allocated width as before.

Verified on B200: test_llama_eagle3_dynamic_tree[True-False] fails at the
parent commit and passes with this change. New unit tests pin the staging
width gate and the max_blocks copy semantics.

Signed-off-by: Kaiyu Xie <26294424+kaiyux@users.noreply.github.com>
…th cap

Signed-off-by: Kaiyu Xie <26294424+kaiyux@users.noreply.github.com>
The bulk copy_ raises on most length mismatches, but a length-1
sequence would broadcast silently. Assert the one-index-per-request
contract explicitly so out-of-tree cache managers fail loudly.

Addresses review feedback on NVIDIA#16313.

Signed-off-by: Kaiyu Xie <26294424+kaiyux@users.noreply.github.com>
@kaiyux
kaiyux force-pushed the perf/decode-step-host-prep-20260713 branch from e4212a2 to 190c73c Compare July 16, 2026 12:13
@kaiyux

kaiyux commented Jul 16, 2026

Copy link
Copy Markdown
Member Author

/bot run --disable-fail-fast

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #59701 [ run ] triggered by Bot. Commit: 190c73c Link to invocation

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #59701 [ run ] completed with state SUCCESS. Commit: 190c73c
/LLM/main/L0_MergeRequest_PR pipeline #48131 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

@kaiyux

kaiyux commented Jul 16, 2026

Copy link
Copy Markdown
Member Author

/bot run --disable-fail-fast

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #59805 [ run ] triggered by Bot. Commit: 190c73c Link to invocation

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #59805 [ run ] completed with state SUCCESS. Commit: 190c73c
/LLM/main/L0_MergeRequest_PR pipeline #48214 completed with status: 'SUCCESS'
Pipeline passed with automatic retried tests. Check the rerun report for details.

CI Report

Link to invocation

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

LGTM

@kaiyux
kaiyux merged commit f4df481 into NVIDIA:main Jul 17, 2026
7 checks passed
@kaiyux
kaiyux deleted the perf/decode-step-host-prep-20260713 branch July 17, 2026 05:13
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.

8 participants