[None][fix] Fix CppMambaHybridCacheManager functional and perf issues - #14003
Conversation
…mamba layers (By Agent) Under PP sharding, a rank can end up with only full-attention layers and no mamba layers. The previous constructor unconditionally allocated SSM / conv state and set up linear-attention metadata, leading to invalid state on these ranks. Take an early-exit after super().__init__ when local_num_mamba_layers == 0: forward num_layers (not mamba_num_layers + num_layers) and the union of mamba+attention layer masks to the parent KVCacheManager, skip all mamba-only setup. Guard prepare_resources / update_mamba_states / _setup_state_indices with the same condition so they no-op on these ranks. Add a regression test that constructs the manager with a real parent KVCacheManager and verifies: early-exit invariants on mgr state, no mamba-only attributes set, and the three guarded methods no-op without raising. Signed-off-by: Xiwen Yu <13230610+VALLIS-NERIA@users.noreply.github.com>
…aph padding and draft-len sentinels (By Agent) The recurrent-state snapshot pool was sized at exactly max_batch_size, which leaves no room for the CUDA graph padding sentinel (CUDA_GRAPH_DUMMY_REQUEST_ID) and the per-draft-len sentinels that CUDAGraphRunner::_get_padded_batch issues during speculative decoding. Reserve one extra slot for the CUDA graph padding dummy, and when a spec_config is present, reserve one additional slot per draft length so all distinct sentinel request IDs can coexist without evicting live recurrent state. The block-reuse path (max_tokens // interval) still overrides this when active. Signed-off-by: Xiwen Yu <13230610+VALLIS-NERIA@users.noreply.github.com>
|
/bot run |
📝 WalkthroughWalkthroughResource manager updates the recurrent-state block pool size calculation to account for CUDA graph padding (+1) and speculative decoding draft tokens. The revised max_snapshots value is used to derive block allocations unless overwritten by existing block-reuse logic. ChangesRecurrent-State Block Pool Sizing
🎯 2 (Simple) | ⏱️ ~8 minutes 🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (2 warnings)
✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
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 `@tensorrt_llm/_torch/pyexecutor/resource_manager.py`:
- Around line 1560-1563: The code uses self.spec_config in the snapshot sizing
branch but KVCacheManager.__init__ never assigns the incoming spec_config to the
instance; update KVCacheManager.__init__ (the constructor) to store the argument
(e.g., self.spec_config = spec_config) before any logic that may reference it so
the branch that adjusts max_snapshots using self.spec_config.max_draft_tokens
will not raise AttributeError.
🪄 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: 7bb47e0c-e115-4317-a5f3-42c778f8b88e
📒 Files selected for processing (1)
tensorrt_llm/_torch/pyexecutor/resource_manager.py
|
PR_Github #47767 [ run ] triggered by Bot. Commit: |
…servation tests (By Agent) Follow-up to the recurrent-state slot reservation: spec_config has no max_draft_tokens attribute (DecodingBaseConfig uses StrictBaseModel with extra="forbid"). The correct field is max_draft_len, which is also the count of additional dummy request ids issued by CUDAGraphRunner._get_padded_batch. Add two unit tests that construct a real CppMambaHybridCacheManager and inspect blocks_per_window[RECURRENT_STATES]: - without spec_config: pool reserves max_batch_size + 1 slots - with spec_config(max_draft_len=N): pool reserves max_batch_size + 1 + N Tests disable block reuse so the override (max_tokens // interval) does not mask the addition under test. Signed-off-by: Xiwen Yu <13230610+VALLIS-NERIA@users.noreply.github.com>
|
/bot run |
|
PR_Github #47768 [ run ] triggered by Bot. Commit: |
|
PR_Github #47768 [ run ] completed with state
|
|
/bot run |
|
PR_Github #47812 [ run ] triggered by Bot. Commit: |
… Agent) Address CodeRabbit review on NVIDIA#14003: the linear-attention sizing branch reads self.spec_config, but KVCacheManager.__init__ never assigned the incoming spec_config argument to self. The code worked only by accident, because CppMambaHybridCacheManager's _setup_mtp_intermediate_states runs before super().__init__ and writes self.spec_config on the partially initialized instance. Any other KVCacheManager path that exercises the linear-attention branch with a spec_config would AttributeError. Assign self.spec_config = spec_config at the top of KVCacheManager.__init__ so the attribute is always present regardless of subclass. Signed-off-by: Xiwen Yu <13230610+VALLIS-NERIA@users.noreply.github.com>
|
Addressed CodeRabbit feedback: store /bot run |
|
/bot run --disable-fail-fast |
|
PR_Github #47826 [ run ] triggered by Bot. Commit: |
… Agent) Two fixes from CodeRabbit's review on NVIDIA#13999: 1. Handle layer_mask=None defensively. The signature has always typed it as Optional[List[bool]], and the new constructor was unconditionally calling layer_mask.copy() / indexing before the zero-mamba guard ran. Treat None as an all-False full-attention mask and validate length against mamba_layer_mask. 2. Seed externally visible mamba fields before the early-return guard. get_mamba_ssm_cache_dtype() reads self.ssm_state_dtype, and the use_replay_state_update property reads self._use_replay_state_update; both were only assigned on the non-early-exit path, so any caller that hit those accessors on a zero-local-mamba rank would have AttributeError'd. Move both assignments above the early-return. Signed-off-by: Xiwen Yu <13230610+VALLIS-NERIA@users.noreply.github.com>
|
PR_Github #47826 [ run ] completed with state
|
…recurrent-cuda-graph-snapshots (By Agent) Brings in the CppMambaHybridCacheManager zero-local-mamba-layers fix and its CodeRabbit follow-ups (PR NVIDIA#13999). Conflict in tests/unittest/_torch/executor/test_mamba_cache_manager.py is resolved by keeping both test sections (recurrent-state pool sizing from this branch, zero-local-mamba early-exit from the merged branch) and unioning the import set. Signed-off-by: Xiwen Yu <13230610+VALLIS-NERIA@users.noreply.github.com>
Signed-off-by: Xiwen Yu <13230610+VALLIS-NERIA@users.noreply.github.com>
… Agent) Address CodeRabbit review on NVIDIA#14003: the linear-attention sizing branch reads self.spec_config, but KVCacheManager.__init__ never assigned the incoming spec_config argument to self. The code worked only by accident, because CppMambaHybridCacheManager's _setup_mtp_intermediate_states runs before super().__init__ and writes self.spec_config on the partially initialized instance. Any other KVCacheManager path that exercises the linear-attention branch with a spec_config would AttributeError. Assign self.spec_config = spec_config at the top of KVCacheManager.__init__ so the attribute is always present regardless of subclass. Signed-off-by: Xiwen Yu <13230610+VALLIS-NERIA@users.noreply.github.com>
|
PR_Github #48803 [ run ] completed with state
|
…dels (By Agent)
`_calculate_max_num_blocks_for_linear_attention` sizes the recurrent-state
slot pool as `max_batch_size + 1`, ignoring `pp_size`. With pipeline
parallelism (e.g. `TestNemotronV3Super::test_nvfp4_parallelism[TP4_PP2]`),
multiple microbatches are in-flight on the same rank simultaneously, each
holding up to `max_batch_size` sequences' Mamba state — so the live-state
slot count must be `max_batch_size * pp_size + 1`. The KVCacheManagerV2
path already does this (`max_num_sequences = max_batch_size * pp_size`);
this commit aligns the linear-attention sizing path with that invariant.
Three call sites are fixed:
1. `KVCacheManager._calculate_max_num_blocks_for_linear_attention` — both
`intercept` (memory reservation in the affine model) and `max_snapshots`
(slot count) now scale with `pp_size`.
2. The `is_linear_attention` branch in the estimation dry-run code path,
which previously used a bare `max_batch_size`.
3. `CppMambaHybridCacheManager.get_cache_size_per_token` — the per-request
fixed-cost intercept that drives upstream `_tokens_for_budget`.
Without this fix, `PP > 1` runs trip `No free block found. This shouldn't
happen!` in `evictionPolicy::getFreeBlock` once requests beyond the first
microbatch arrive at the pool. Added a regression unit test that asserts
`recurrent_primary >= max_batch_size * pp_size + 1` for `pp_size ∈ {2, 4}`,
and verified the fix end-to-end with the TP4_PP2 model run on a TP2_PP2
configuration locally (test passed in 8m53s, no `No free block` error).
Signed-off-by: Xiwen Yu <13230610+VALLIS-NERIA@users.noreply.github.com>
|
/bot run --disable-fail-fast --stage-list "DGX_B200-4_GPUs-PyTorch-2, DGX_B200-8_GPUs-PyTorch-1, GB200-4_GPUs-PyTorch-1, GB200-4_GPUs-PyTorch-2" |
|
PR_Github #48927 [ run ] triggered by Bot. Commit: |
|
PR_Github #48927 [ run ] completed with state
|
|
/bot run --disable-fail-fast --stage-list "DGX_B200-4_GPUs-PyTorch-2, DGX_B200-8_GPUs-PyTorch-1, GB200-4_GPUs-PyTorch-1, GB200-4_GPUs-PyTorch-2" |
|
PR_Github #49315 [ run ] triggered by Bot. Commit: |
|
PR_Github #49315 [ run ] completed with state |
|
/bot run --disable-fail-fast |
|
PR_Github #49535 [ run ] triggered by Bot. Commit: |
yuxianq
left a comment
There was a problem hiding this comment.
tensorrt_llm/_torch/pyexecutor/_util.py LGTM
commit c9933a8 Author: Xiwen Yu <13230610+VALLIS-NERIA@users.noreply.github.com> Date: Mon May 18 17:58:20 2026 +0800 [None][fix] Scale recurrent-state pool by pp_size for hybrid Mamba models (By Agent) `_calculate_max_num_blocks_for_linear_attention` sizes the recurrent-state slot pool as `max_batch_size + 1`, ignoring `pp_size`. With pipeline parallelism (e.g. `TestNemotronV3Super::test_nvfp4_parallelism[TP4_PP2]`), multiple microbatches are in-flight on the same rank simultaneously, each holding up to `max_batch_size` sequences' Mamba state — so the live-state slot count must be `max_batch_size * pp_size + 1`. The KVCacheManagerV2 path already does this (`max_num_sequences = max_batch_size * pp_size`); this commit aligns the linear-attention sizing path with that invariant. Three call sites are fixed: 1. `KVCacheManager._calculate_max_num_blocks_for_linear_attention` — both `intercept` (memory reservation in the affine model) and `max_snapshots` (slot count) now scale with `pp_size`. 2. The `is_linear_attention` branch in the estimation dry-run code path, which previously used a bare `max_batch_size`. 3. `CppMambaHybridCacheManager.get_cache_size_per_token` — the per-request fixed-cost intercept that drives upstream `_tokens_for_budget`. Without this fix, `PP > 1` runs trip `No free block found. This shouldn't happen!` in `evictionPolicy::getFreeBlock` once requests beyond the first microbatch arrive at the pool. Added a regression unit test that asserts `recurrent_primary >= max_batch_size * pp_size + 1` for `pp_size ∈ {2, 4}`, and verified the fix end-to-end with the TP4_PP2 model run on a TP2_PP2 configuration locally (test passed in 8m53s, no `No free block` error). Signed-off-by: Xiwen Yu <13230610+VALLIS-NERIA@users.noreply.github.com> commit 7a6e90f Author: Xiwen Yu <13230610+VALLIS-NERIA@users.noreply.github.com> Date: Mon May 18 01:45:33 2026 +0800 [None][fix] Cap CppMamba warmup tokens by recurrent-state pool capacity (By Agent) `_create_cuda_graph_warmup_request` constructs the longest dummy request using `available_tokens` returned by `kv_cache_manager.get_num_available_tokens`. For `CppMambaHybridCacheManager`, the parent's implementation only bounds this by the attention KV cache, so the long dummy's `token_num` could balloon to near `max_seq_len` (262K for Qwen3.5-397B-A17B). With block reuse on and `mamba_state_cache_interval=256`, that one dummy then needs ~1024 recurrent-state blocks, exhausting the RS pool when stacked with the (batch_size - 1) short dummies in the same warmup batch and tripping `No free block found` in `evictionPolicy::getFreeBlock`. Override `get_num_available_tokens` in CppMambaHybridCacheManager to also clamp by `(rs_free_blocks - 1) * states_snapshot_interval` whenever the snapshot interval is positive, leaving one block of headroom for the always-allocated trailing snapshot. The override is a no-op when `enable_block_reuse=False` (interval == 0). Verified with `tests/integration/defs/accuracy/test_llm_api_pytorch.py::TestQwen3_5_397B_A17B::test_nvfp4[tep4_block_reuse]`. Signed-off-by: Xiwen Yu <13230610+VALLIS-NERIA@users.noreply.github.com> commit 717edeb Author: Xiwen Yu <13230610+VALLIS-NERIA@users.noreply.github.com> Date: Sat May 16 16:27:02 2026 +0800 fix schedule issue, improve runtime perf, allow change manager Signed-off-by: Xiwen Yu <13230610+VALLIS-NERIA@users.noreply.github.com> commit 1813212 Author: Xiwen Yu <13230610+VALLIS-NERIA@users.noreply.github.com> Date: Fri May 15 08:47:09 2026 +0800 [None][fix] Address recurrent-state pool sizing and layer-first transfer bugs (By Agent) Concern 1 (kvCacheTransferManager.cpp): In the layer-first cudaMemcpy2DAsync path, source and destination pitches were both derived from pool.primaryPtr. For host-offload transfers, the secondary pool has mNumSecondaryBlocks which can differ from mNumPrimaryBlocks, giving a different per-layer stride. Fix: compute srcLayerStrideBytes and dstLayerStrideBytes independently from srcPool and dstPool, and use them as separate pitches in cudaMemcpy2DAsync. Concern 2 (resource_manager.py): In _calculate_max_num_blocks_for_linear_attention, the block-reuse branch unconditionally overwrote max_snapshots with max_tokens // interval, discarding the live-state + CUDA-graph-padding floor (max_batch_size + 1 + max_draft_len) set earlier. Fix: use max(max_tokens // interval, max_snapshots) to preserve the floor. Add a regression test (enable_block_reuse=True) that would have caught the dropped floor: with max_batch_size=4 and max_tokens=512/interval=256, the naive branch returns 2 slots (< required floor of 5). Signed-off-by: Xiwen Yu <13230610+VALLIS-NERIA@users.noreply.github.com> commit e00c230 Merge: 69899c5 be4f57e Author: Xiwen Yu <13230610+VALLIS-NERIA@users.noreply.github.com> Date: Wed May 13 18:12:00 2026 +0800 Merge branch 'main' into user/xiweny/mamba-recurrent-cuda-graph-snapshots Signed-off-by: Xiwen Yu <13230610+VALLIS-NERIA@users.noreply.github.com> commit 69899c5 Author: Xiwen Yu <13230610+VALLIS-NERIA@users.noreply.github.com> Date: Wed May 13 18:01:04 2026 +0800 fix replay check on sm120 Signed-off-by: Xiwen Yu <13230610+VALLIS-NERIA@users.noreply.github.com> commit 3f0e36c Author: Xiwen Yu <13230610+VALLIS-NERIA@users.noreply.github.com> Date: Wed May 13 17:57:48 2026 +0800 fix missing is_draft Signed-off-by: Xiwen Yu <13230610+VALLIS-NERIA@users.noreply.github.com> commit 516ea84 Author: Xiwen Yu <13230610+VALLIS-NERIA@users.noreply.github.com> Date: Tue May 12 23:16:54 2026 +0800 reduce sync overhead Signed-off-by: Xiwen Yu <13230610+VALLIS-NERIA@users.noreply.github.com> commit ace03a2 Merge: 5a85831 5aad39a Author: Xiwen Yu <13230610+VALLIS-NERIA@users.noreply.github.com> Date: Tue May 12 21:27:53 2026 +0800 Merge branch 'user/xiweny/mamba-hybrid-zero-mamba-layers' into mamba-recurrent-cuda-graph-snapshots (By Agent) Brings in the CppMambaHybridCacheManager zero-local-mamba-layers fix and its CodeRabbit follow-ups (PR NVIDIA#13999). Conflict in tests/unittest/_torch/executor/test_mamba_cache_manager.py is resolved by keeping both test sections (recurrent-state pool sizing from this branch, zero-local-mamba early-exit from the merged branch) and unioning the import set. Signed-off-by: Xiwen Yu <13230610+VALLIS-NERIA@users.noreply.github.com> commit 5aad39a Author: Xiwen Yu <13230610+VALLIS-NERIA@users.noreply.github.com> Date: Tue May 12 11:15:21 2026 +0800 [None][fix] CppMambaHybridCacheManager: address CodeRabbit review (By Agent) Two fixes from CodeRabbit's review on NVIDIA#13999: 1. Handle layer_mask=None defensively. The signature has always typed it as Optional[List[bool]], and the new constructor was unconditionally calling layer_mask.copy() / indexing before the zero-mamba guard ran. Treat None as an all-False full-attention mask and validate length against mamba_layer_mask. 2. Seed externally visible mamba fields before the early-return guard. get_mamba_ssm_cache_dtype() reads self.ssm_state_dtype, and the use_replay_state_update property reads self._use_replay_state_update; both were only assigned on the non-early-exit path, so any caller that hit those accessors on a zero-local-mamba rank would have AttributeError'd. Move both assignments above the early-return. Signed-off-by: Xiwen Yu <13230610+VALLIS-NERIA@users.noreply.github.com> commit 5a85831 Author: Xiwen Yu <13230610+VALLIS-NERIA@users.noreply.github.com> Date: Tue May 12 09:41:25 2026 +0800 [None][fix] KVCacheManager: store spec_config on self in __init__ (By Agent) Address CodeRabbit review on NVIDIA#14003: the linear-attention sizing branch reads self.spec_config, but KVCacheManager.__init__ never assigned the incoming spec_config argument to self. The code worked only by accident, because CppMambaHybridCacheManager's _setup_mtp_intermediate_states runs before super().__init__ and writes self.spec_config on the partially initialized instance. Any other KVCacheManager path that exercises the linear-attention branch with a spec_config would AttributeError. Assign self.spec_config = spec_config at the top of KVCacheManager.__init__ so the attribute is always present regardless of subclass. Signed-off-by: Xiwen Yu <13230610+VALLIS-NERIA@users.noreply.github.com> commit 3310de0 Author: Xiwen Yu <13230610+VALLIS-NERIA@users.noreply.github.com> Date: Mon May 11 23:46:42 2026 +0800 [None][fix] KVCacheManager: fix max_draft_tokens typo and add slot reservation tests (By Agent) Follow-up to the recurrent-state slot reservation: spec_config has no max_draft_tokens attribute (DecodingBaseConfig uses StrictBaseModel with extra="forbid"). The correct field is max_draft_len, which is also the count of additional dummy request ids issued by CUDAGraphRunner._get_padded_batch. Add two unit tests that construct a real CppMambaHybridCacheManager and inspect blocks_per_window[RECURRENT_STATES]: - without spec_config: pool reserves max_batch_size + 1 slots - with spec_config(max_draft_len=N): pool reserves max_batch_size + 1 + N Tests disable block reuse so the override (max_tokens // interval) does not mask the addition under test. Signed-off-by: Xiwen Yu <13230610+VALLIS-NERIA@users.noreply.github.com> commit 992ec1b Author: Xiwen Yu <13230610+VALLIS-NERIA@users.noreply.github.com> Date: Mon May 11 23:34:04 2026 +0800 [None][fix] KVCacheManager: reserve recurrent-state slots for CUDA graph padding and draft-len sentinels (By Agent) The recurrent-state snapshot pool was sized at exactly max_batch_size, which leaves no room for the CUDA graph padding sentinel (CUDA_GRAPH_DUMMY_REQUEST_ID) and the per-draft-len sentinels that CUDAGraphRunner::_get_padded_batch issues during speculative decoding. Reserve one extra slot for the CUDA graph padding dummy, and when a spec_config is present, reserve one additional slot per draft length so all distinct sentinel request IDs can coexist without evicting live recurrent state. The block-reuse path (max_tokens // interval) still overrides this when active. Signed-off-by: Xiwen Yu <13230610+VALLIS-NERIA@users.noreply.github.com> commit ac30ded Author: Xiwen Yu <13230610+VALLIS-NERIA@users.noreply.github.com> Date: Mon May 11 20:26:14 2026 +0800 [None][fix] CppMambaHybridCacheManager: handle ranks with zero local mamba layers (By Agent) Under PP sharding, a rank can end up with only full-attention layers and no mamba layers. The previous constructor unconditionally allocated SSM / conv state and set up linear-attention metadata, leading to invalid state on these ranks. Take an early-exit after super().__init__ when local_num_mamba_layers == 0: forward num_layers (not mamba_num_layers + num_layers) and the union of mamba+attention layer masks to the parent KVCacheManager, skip all mamba-only setup. Guard prepare_resources / update_mamba_states / _setup_state_indices with the same condition so they no-op on these ranks. Add a regression test that constructs the manager with a real parent KVCacheManager and verifies: early-exit invariants on mgr state, no mamba-only attributes set, and the three guarded methods no-op without raising. Signed-off-by: Xiwen Yu <13230610+VALLIS-NERIA@users.noreply.github.com> Signed-off-by: Wanli Jiang <35160485+Wanli-Jiang@users.noreply.github.com>
|
PR_Github #49535 [ run ] completed with state
|
…LM_DISABLE_MPI=1 (By Agent) test_cpp_hybrid_recurrent_pool_scales_with_pp_size builds a Mapping with world_size=pp_size>1. When pytest runs with --run-ray (conftest sets TLLM_DISABLE_MPI=1), Mapping factory resolves to DeviceMeshTopology whose pp_rank requires torch.distributed initialisation that is not available in this single-process unit test. The PP-pool-sizing behaviour exercised by the test is orthogonal to the Ray orchestrator, so skip cleanly in that session mode rather than mocking the distributed stack. Signed-off-by: Xiwen Yu <13230610+VALLIS-NERIA@users.noreply.github.com>
…urrent-cuda-graph-snapshots Signed-off-by: Xiwen Yu <13230610+VALLIS-NERIA@users.noreply.github.com> # Conflicts: # tensorrt_llm/_torch/pyexecutor/_util.py
|
/bot run --disable-fail-fast |
|
PR_Github #49684 [ run ] triggered by Bot. Commit: |
|
PR_Github #49684 [ run ] completed with state
|
|
/bot run |
|
PR_Github #49752 [ run ] triggered by Bot. Commit: |
|
PR_Github #49752 [ run ] completed with state
|
|
/bot run --disable-fail-fast |
|
PR_Github #49760 [ run ] triggered by Bot. Commit: |
|
PR_Github #49760 [ run ] completed with state |
…NVIDIA#14003) Signed-off-by: Xiwen Yu <13230610+VALLIS-NERIA@users.noreply.github.com>
…NVIDIA#14003) Signed-off-by: Xiwen Yu <13230610+VALLIS-NERIA@users.noreply.github.com>
Summary
Fixes a set of functional and performance issues in
CppMambaHybridCacheManagerand the recurrent-state cache path.Functional fixes
Zero local mamba layers under PP sharding (
ac30ded849)Under pipeline-parallel sharding, a rank can end up with only full-attention layers and no mamba layers. The previous constructor unconditionally allocated SSM/conv state and set up linear-attention metadata, leaving such ranks in an invalid state. Added an early-exit after
super().__init__whenlocal_num_mamba_layers == 0, forwarded the union of mamba+attention layer masks to the parentKVCacheManager, and guardedprepare_resources/update_mamba_statesto no-op on those ranks.Reserve recurrent-state slots for CUDA graph padding and draft-len sentinels (
992ec1bd08)The recurrent-state snapshot pool (
_calculate_max_num_blocks_for_linear_attention→max_snapshots) was sized at exactlymax_batch_size, which doesn't reserve room for:CUDA_GRAPH_DUMMY_REQUEST_ID) — needs 1 extra slot.CUDAGraphRunner::_get_padded_batchissues during speculative decoding — needs one extra slot per draft length.Under load, this caused the padding sentinels to evict live recurrent state and surface as block-allocation failures. Added
+1for the CUDA graph padding sentinel and+ spec_config.max_draft_tokenswhen aspec_configis present. The block-reuse override (max_tokens // interval) still wins when active.Follow-ups on the slot-reservation fix
3310de0a0e— fixed amax_draft_tokenstypo and added unit tests covering the slot reservation logic.5a858312ce— storespec_configonselfin__init__so the override path can read it.5aad39a889— addressed CodeRabbit review comments on the above.Performance fixes (
516ea84eaa)Several host/device sync points were eliminated from the mamba-hybrid prep path. With Qwen3.5-A17B-NVFP4 + MTP Eagle one-model + CUDA graphs, nsys showed
numGenReq × (1 + max_draft_len)cudaMemcpyAsync+cudaStreamSynchronizepairs per iteration sitting inside_prepare_inputs, plus arefresh_blocksstream sync at the tail ofprepare_resourcesthat blocked subsequent prep work.Mamba2Metadata.state_indices: alias the cache manager's CUDA buffer instead of per-element D2H.The previous code did
for i, idx in enumerate(indices): state_indices_cpu[i] = idx, whereindicesisCppMambaHybridCacheManager.cuda_state_indices(a CUDA tensor). Iterating a CUDA tensor produces 0-d CUDA slices, and assigning each to a CPU tensor issues acudaMemcpyAsync+cudaStreamSynchronizeper element. Replaced with a direct reference assignment (self.state_indices = indices) when the source is on CUDA, guarded by adata_ptr()invariant assertion to catch future buffer reallocations that would silently break CUDA graph replays. CPU/list paths keep the original behavior.KVCacheTransferManager::copyBlocklayer-first path: onecudaMemcpy2DAsyncinstead of N async memcpys.Pool layout is
{numLayers, numBlocks, kvFactor, blockSize}. For a fixed block index, per-layer slices are equal-length rows separated by stridenumBlocks * rowBytes. Replaced the per-layercudaMemcpyAsyncloop with a single pitched 2D copy.Defer
refresh_blocks()(stream sync) until just before forward.Split
CppMambaHybridCacheManager._prepare_resourcesinto two phases: (1) fire allcopy_linear_attention_blockonboards asynchronously and record whether any actually scheduled a transfer, and (2) a newflush_state_transfers()method that callsrefresh_blocks()only when there are pending transfers. The flush is invoked at the end ofMamba2Metadata.prepare(), so the rest of_prepare_tp_inputs(host work + H2D copies) overlaps with the in-flight onboard transfers.To support the "skip if nothing scheduled" optimization,
KVCacheManager::copyLinearAttentionBlocknow returnsboolindicating whether at least one onboard was actually issued. The 3 layers of the call (KVCacheManager→BlockManager→WindowBlockManager) all returnbool; pybind reflects the new return type automatically.Test plan
tests/unittest/_torch/executor/test_mamba_cache_manager.pycovers slot reservation across CUDA-graph + draft-len combinations.cudaMemcpyAsync+ sync pairs gone, and therefresh_blocksstream sync moved out of the critical path.Summary by CodeRabbit