Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 11 additions & 15 deletions tensorrt_llm/_torch/pyexecutor/mamba_cache_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -3066,8 +3066,7 @@ def __init__(
LayerId(first_mamba_local_layer), MambaRole.SSM_STATE)
num_ssm_slots = ((num_ssm_pages + self._ssm_page_index_scale - 1) //
self._ssm_page_index_scale)
required_live_slots = (self._max_resident_sequences() +
self._num_reserved_dummy_slots)
required_live_slots = self._num_required_state_slots()
if num_ssm_slots < required_live_slots:
KVCacheManagerV2.shutdown(self)
raise ValueError(
Expand Down Expand Up @@ -3183,6 +3182,11 @@ def _get_pool_roles(self,
def _max_resident_sequences(self) -> int:
return self.max_batch_size * self.mapping.pp_size

def _num_required_state_slots(self) -> int:
"""Return the SSM slots that must always be live: one per resident
request lineage plus every reserved dummy slot."""
return self._max_resident_sequences() + self._num_reserved_dummy_slots

def _mamba_state_bytes_per_slot(self) -> int:
return self.local_num_mamba_layers * (self.ssm_bytes + self.conv_bytes)

Expand Down Expand Up @@ -3296,9 +3300,8 @@ def _minimum_live_gpu_quota(self) -> int:
"""Return the minimum quota for live states and one attention page."""
attention_block_quota = (self._attention_cache_bytes_per_token() *
self.tokens_per_block)
num_state_slots = (self._max_resident_sequences() +
self._num_reserved_dummy_slots)
state_quota = num_state_slots * self._mamba_state_bytes_per_slot()
state_quota = (self._num_required_state_slots() *
self._mamba_state_bytes_per_slot())
return max(
self._get_quota_from_max_tokens(0),
state_quota + attention_block_quota,
Expand Down Expand Up @@ -3332,10 +3335,8 @@ def _build_cache_config(
],
)

dummy_requests = [
KVCacheDesc(capacity=0, history_length=0)
for _ in range(self._num_reserved_dummy_slots)
]
empty_desc = KVCacheDesc(capacity=0, history_length=0)

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.

Nit: KVCacheDesc is a plain (non-frozen) dataclass, so [empty_desc] * n now shares one instance across both dummy_requests and the SSM floor BatchDesc. Harmless today since nothing mutates descs, but the comprehension it replaced was aliasing-proof for free.

dummy_requests = [empty_desc] * self._num_reserved_dummy_slots
constraints = [
replace(
batch,
Expand All @@ -3362,14 +3363,9 @@ def _build_cache_config(
# / __init__). Add a min-slots constraint of zero-capacity requests:
# these cost no attention pages but reserve one SSM slot each.
if any(isinstance(layer, SsmLayerConfig) for layer in layers):
ssm_floor_slots = (self._max_resident_sequences() +
self._num_reserved_dummy_slots)
constraints = [

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 block, including the _max_resident_sequences() + _num_reserved_dummy_slots floor, is already present at the merge base — the change here is just the helper extraction. If that's right, the PR title/description overstate it.

*constraints,
BatchDesc([
KVCacheDesc(capacity=0, history_length=0)
for _ in range(ssm_floor_slots)
]),
BatchDesc([empty_desc] * self._num_required_state_slots()),
]
return replace(
config,
Expand Down
1 change: 0 additions & 1 deletion tests/integration/test_lists/waives.txt
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,6 @@ full:GB200/accuracy/test_llm_api_pytorch.py::TestDeepSeekV3Lite::test_bfloat16[m
full:GB200/accuracy/test_llm_api_pytorch.py::TestDeepSeekV3Lite::test_cute_dsl_bf16_gemm[cuda_graph=True] SKIP (https://nvbugs/6525897)
full:GB200/accuracy/test_llm_api_pytorch.py::TestMiniMaxM3::test_mxfp8[use_msa=False] SKIP (https://nvbugs/6479471)
full:GB200/accuracy/test_llm_api_pytorch.py::TestMiniMaxM3::test_nvfp4[use_msa=False] SKIP (https://nvbugs/6479471)

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.

Un-waiving a P0 GB200 test on the strength of a behavior-preserving refactor looks premature. If the intent is to unwaive because the real fix landed elsewhere, please say which commit fixed it and cite passing runs of this exact node ID (and close the bug), since the recorded failure mode is a flashinfer JIT-cache error rather than a cache-manager sizing error.

full:GB200/accuracy/test_llm_api_pytorch.py::TestNemotronV3Ultra::test_nvfp4_4gpus_static_eplb[moe_backend=TRTLLM] SKIP (https://nvbugs/6525898)
full:GB200/accuracy/test_llm_api_pytorch.py::TestQwen3_30B_A3B::test_nvfp4[dep4_latency_moe_cutlass-torch_compile=True] SKIP (https://nvbugs/5929339)
full:GB200/accuracy/test_llm_api_pytorch.py::TestQwen3_5_35B_A3B::test_fp8_moe_dflash SKIP (https://nvbugs/6316985)
full:GB200/accuracy/test_llm_api_pytorch_multimodal.py::TestExaone4_5_33B::test_auto_dtype[forced_chunked_prefill] SKIP (https://nvbugs/6597570)
Expand Down
48 changes: 48 additions & 0 deletions tests/unittest/_torch/executor/test_mamba_cache_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -2058,6 +2058,54 @@ def _slot_sizes(stat):
assert high_mamba_allocation[1] < low_mamba_allocation[1]


def test_v2_hybrid_constrains_ssm_pool_to_live_slot_floor():

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 file isn't referenced by any tests/integration/test_lists/test-db/*.yml — that directory is enrolled per-file (see unittest/_torch/executor/test_kv_cache_budget_split.py etc. in l0_a10.yml). As-is the new regression test never runs in pre-merge CI; add the file to an appropriate l0 list.

"""The SSM pool floor must be a constraint, not just a typical_step ratio.

With avg_seq_len unset the base config emits no constraints, so only the
fallback typical_step steers the pool ratio. A ratio is not a floor, so the
grain split could round the SSM pool below the live/dummy slot count that
__init__ validates (https://nvbugs/6525898). Assert the floor reaches the
storage manager as a constraint carrying no attention capacity.
"""
mgr = object.__new__(MambaHybridCacheManagerV2)
mgr.kv_cache_type = CacheTypeCpp.SELF
mgr.head_dim_per_layer = [64, 64]
mgr.pp_layers = [0, 1]
mgr._mamba_layer_mask = [True, False]
mgr.ssm_bytes = 64
mgr.conv_bytes = 32
mgr.max_attention_window_vec = [128, 128]
mgr.max_batch_size = 32
mgr.mapping = Mapping(world_size=2, rank=0, tp_size=1, pp_size=2)
mgr.max_seq_len = 128
mgr.max_num_tokens = 128
mgr.tokens_per_block = 32
mgr.num_local_layers = 2
mgr.local_num_mamba_layers = 1
mgr._num_reserved_dummy_slots = 2
mgr.dtype = DataType.HALF
mgr.enable_swa_scratch_reuse = False
mgr.enable_stats = False
mgr.num_extra_kv_tokens = 0
mgr.get_layer_bytes_per_token = lambda **kwargs: 8
mgr._minimum_live_gpu_quota = lambda: 0
mgr.kv_cache_config = KvCacheConfig(enable_block_reuse=False)

base_config = mgr._build_base_config(
mgr.kv_cache_config,
tokens_per_block=32,
cache_tiers=[GpuCacheTierConfig(quota=1 << 20)],
)
# avg_seq_len is unset, so nothing pins the pool sizes yet.
assert base_config.typical_step is None
assert not base_config.constraints

config = mgr._build_cache_config(base_config)

# 32 max_batch_size * pp_size 2 resident lineages + 2 reserved dummy slots.
assert config.constraints == [BatchDesc([KVCacheDesc(capacity=0, history_length=0)] * 66)]


# ---------------------------------------------------------------------------
# Cpp/V2 Mamba hybrid managers: recurrent-state allocation and reuse
#
Expand Down
Loading