Skip to content
Merged
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
9 changes: 9 additions & 0 deletions cpp/include/tensorrt_llm/batch_manager/llmRequest.h
Original file line number Diff line number Diff line change
Expand Up @@ -1908,6 +1908,15 @@ class GenericLlmRequest
return mPerfMetrics.kvCacheMetrics.numNewAllocatedBlocks;
}

void updateKvCachePerfMetrics(
SizeType32 allocTotalBlocks, SizeType32 allocNewBlocks, SizeType32 reusedBlocks, SizeType32 missedBlocks)
{
updateAllocTotalBlocksPerRequest(allocTotalBlocks);
updateAllocNewBlocksPerRequest(allocNewBlocks);
updateReusedBlocksPerRequest(reusedBlocks);
updateMissedBlocksPerRequest(missedBlocks);
}

void updateReusedBlocksPerRequest(SizeType32 reusedBlocksPerRequest)
{
mPerfMetrics.kvCacheMetrics.numReusedBlocks += reusedBlocksPerRequest;
Expand Down
8 changes: 8 additions & 0 deletions cpp/tensorrt_llm/nanobind/batch_manager/bindings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -191,11 +191,19 @@ void initBindings(nb::module_& m)
.def_prop_ro("is_disagg_context_complete_state", &GenLlmReq::isDisaggContextCompleteState)
.def_prop_ro("stage", &GenLlmReq::getRequestStage)
.def_prop_ro("kv_cache_transfer_time_ms", &GenLlmReq::getKvCacheTransferTimeMS)
.def_prop_ro("kv_cache_transfer_start", &GenLlmReq::getKvCacheTransferStart)
.def_prop_ro("kv_cache_transfer_end", &GenLlmReq::getKvCacheTransferEnd)
.def_prop_ro("kv_cache_size", &GenLlmReq::getKvCacheSize)
.def("set_kv_cache_transfer_start", &GenLlmReq::setKvCacheTransferStart, nb::arg("time"))
.def("set_kv_cache_transfer_end", &GenLlmReq::setKvCacheTransferEnd, nb::arg("time"))
.def("set_kv_cache_size", &GenLlmReq::setKvCacheSize, nb::arg("target_buffer_size"))
.def("update_kv_cache_size", &GenLlmReq::updateKvCacheSize, nb::arg("target_buffer_size"))
.def_prop_ro("avg_decoded_tokens_per_iter", &GenLlmReq::getAvgDecodedTokensPerIter)
.def_prop_ro("alloc_total_blocks", &GenLlmReq::getAllocTotalBlocksPerRequest)
.def_prop_ro("alloc_new_blocks", &GenLlmReq::getAllocNewBlocksPerRequest)
.def("alloc_context_logits", &GenLlmReq::allocContextLogitsHost, nb::arg("vocab_size"), nb::arg("logit_dtype"))
.def("update_kv_cache_perf_metrics", &GenLlmReq::updateKvCachePerfMetrics, nb::arg("alloc_total_blocks"),
nb::arg("alloc_new_blocks"), nb::arg("reused_blocks"), nb::arg("missed_blocks"))
.def_prop_ro("reused_blocks", &GenLlmReq::getReusedBlocksPerRequest)
.def_prop_ro("missed_blocks", &GenLlmReq::getMissedBlocksPerRequest)
.def_prop_ro("kv_cache_hit_rate", &GenLlmReq::getKVCacheHitRatePerRequest)
Expand Down
1 change: 1 addition & 0 deletions tensorrt_llm/_torch/auto_deploy/_compat.py
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,7 @@ class KvCacheConfig(BaseModel):
cross_kv_cache_fraction: Optional[float] = None
secondary_offload_min_priority: Optional[int] = None
event_buffer_max_size: int = 0
kv_cache_event_hash_algo: str = "auto"
enable_partial_reuse: bool = True
copy_on_partial_reuse: bool = True
use_uvm: bool = False
Expand Down
91 changes: 46 additions & 45 deletions tensorrt_llm/_torch/pyexecutor/_util.py
Original file line number Diff line number Diff line change
Expand Up @@ -236,8 +236,6 @@ def __init__(
self._max_beam_width = max_beam_width
self._kv_connector_manager = kv_connector_manager
self._llm_args = llm_args
# For V2 fallback use only, will be removed after V2 is stable
self._cache_transceiver_config = llm_args.cache_transceiver_config
self._speculative_config = speculative_config
self._sparse_attention_config = sparse_attention_config
self._tokens_per_block = tokens_per_block
Expand All @@ -247,6 +245,7 @@ def __init__(
self._dummy_reqs = None
self._profiling_stage_data = profiling_stage_data
self._is_disagg = is_disagg
self._cache_transceiver_config = llm_args.cache_transceiver_config
self._execution_stream = execution_stream
self._kv_cache_manager_cls = self._get_model_kv_cache_manager_cls(
model_engine)
Expand All @@ -261,30 +260,46 @@ def _get_model_kv_cache_manager_cls(
kv_cache_config = (kv_cache_config_override if kv_cache_config_override
is not None else self._kv_cache_config)
model_config = model_engine.model.model_config
config = model_config.pretrained_config
cls = get_kv_cache_manager_cls(
model_config,
kv_cache_config,
is_disagg=self._is_disagg,
cache_transceiver_config=self._cache_transceiver_config)
cls = self._fallback_if_unsupported_kv_cache_manager_v2(
cls, model_config, kv_cache_config)
# The V1-route hybrid mamba managers (disagg, TRTLLM_USE_CPP_MAMBA,
# TRTLLM_USE_PY_MAMBA, or one-model speculative decoding) keep mamba
# state in a separate cache that doesn't honor block reuse. Warn at
# the routing site so users see the warning where the decision is
# actually made.
if is_hybrid_linear(model_engine.model.model_config.pretrained_config) \
and kv_cache_config.enable_block_reuse:
uses_v1_mamba_route = self._is_disagg \
or os.environ.get('TRTLLM_USE_CPP_MAMBA', '0') == '1' \
or os.environ.get('TRTLLM_USE_PY_MAMBA', '0') == '1' \
or self._speculative_config is not None
if uses_v1_mamba_route:
logger.warning(
"Block reuse does not work with MTP for hybrid linear models "
"when using the legacy MambaCacheManager (TRTLLM_USE_CPP_MAMBA=1)"
)
return cls

def _fallback_if_unsupported_kv_cache_manager_v2(
self,
kv_cache_manager_cls,
model_config: ModelConfig,
kv_cache_config: Optional[KvCacheConfig] = None):
config = model_config.pretrained_config
# Use ``issubclass`` rather than identity equality so V2 subclasses
# (e.g. ``MiniMaxM3KVCacheManagerV2`` from the sparse-attention path)
# also go through the V2-incompatible-feature gate below. The earlier
# ``cls == KVCacheManagerV2`` check silently bypassed all V2 subclasses
# and let the bare ``assert event_buffer_max_size == 0`` in
# ``KVCacheManagerV2.__init__`` trip at executor construction with
# a useless error message.
if issubclass(cls, KVCacheManagerV2):
# also go through the V2-incompatible-feature gate below.
if issubclass(kv_cache_manager_cls, KVCacheManagerV2):
incompat: List[str] = []
if self._kv_connector_manager is not None:
incompat.append("kv_connector_manager")
if self._max_beam_width is not None and self._max_beam_width > 1:
incompat.append("beam_width > 1")
if kv_cache_config.event_buffer_max_size > 0:
incompat.append("event_buffer_max_size > 0")
if (self._cache_transceiver_config is not None
and self._cache_transceiver_config.backend is not None):
incompat.append("cache_transceiver")
if incompat:
incompat_str = ", ".join(incompat)
# Some models are structurally bound to V2 and cannot fall
Expand Down Expand Up @@ -315,24 +330,8 @@ def _get_model_kv_cache_manager_cls(
logger.warning(
"KVCacheManagerV2 is not supported with %s. "
"Falling back to KVCacheManager.", incompat_str)
cls = KVCacheManager
# The V1-route hybrid mamba managers (disagg, TRTLLM_USE_CPP_MAMBA,
# TRTLLM_USE_PY_MAMBA, or one-model speculative decoding) keep mamba
# state in a separate cache that doesn't honor block reuse. Warn at
# the routing site so users see the warning where the decision is
# actually made.
if is_hybrid_linear(model_engine.model.model_config.pretrained_config) \
and kv_cache_config.enable_block_reuse:
uses_v1_mamba_route = self._is_disagg \
or os.environ.get('TRTLLM_USE_CPP_MAMBA', '0') == '1' \
or os.environ.get('TRTLLM_USE_PY_MAMBA', '0') == '1' \
or self._speculative_config is not None
if uses_v1_mamba_route:
logger.warning(
"Block reuse does not work with MTP for hybrid linear models "
"when using the legacy MambaCacheManager (TRTLLM_USE_CPP_MAMBA=1)"
)
return cls
return KVCacheManager
return kv_cache_manager_cls

def _per_manager_cache_cost(self,
manager_cls,
Expand Down Expand Up @@ -943,18 +942,8 @@ def _create_one_model_draft_kv_cache_manager(
# Get the appropriate KV cache manager class for the draft model
draft_kv_cache_manager_cls = get_kv_cache_manager_cls(
effective_draft_config, draft_kv_config, is_disagg=self._is_disagg)

# Use V2 if enabled and the base class is KVCacheManager
if draft_kv_cache_manager_cls == KVCacheManagerV2:
if self._kv_connector_manager is not None or (
self._max_beam_width is not None and self._max_beam_width
> 1) or draft_kv_config.event_buffer_max_size > 0 or (
self._cache_transceiver_config is not None
and self._cache_transceiver_config.backend is not None):
logger.warning(
"KVCacheManagerV2 is not supported with disaggregated serving or beam width > 1 or event buffer max size > 0 or disagg config. "
"Falling back to KVCacheManager for draft model.")
draft_kv_cache_manager_cls = KVCacheManager
draft_kv_cache_manager_cls = self._fallback_if_unsupported_kv_cache_manager_v2(
draft_kv_cache_manager_cls, effective_draft_config, draft_kv_config)

estimating_kv_cache = estimating_kv_cache and not self._skip_est
# For MTP with models using sparse attention (e.g., DeepSeek V3 with DSA),
Expand Down Expand Up @@ -2086,6 +2075,18 @@ def create_py_executor_instance(
if cross_kv_cache_manager is not None else
LlmRequestState.CONTEXT_INIT)

# V2 scheduler uses scheduler_capacity as the per-iteration request
# budget (BudgetTracker.max_num_requests). Unlike V1 which has a
# separate CapacityScheduler (needs pp_size * max_batch_size to hold
# requests across PP stages) and MicroBatchScheduler (uses
# max_batch_size for per-forward batch limit), V2 merges both into
# one loop. PP on-the-fly is handled by inflight_request_ids
# filtering, so its budget should be based on max_batch_size, not
# max_num_sequences (which includes the pp_size multiplier).
v2_scheduler_capacity = max_batch_size
if v2_scheduler_capacity == 1 and mapping.enable_attention_dp and kv_cache_manager:
v2_scheduler_capacity += 1

if isinstance(kv_cache_manager, KVCacheManagerV2):
# V2: interleaved scheduler handles both capacity and budget
draft_kv_cache_manager = resources.get(
Expand All @@ -2101,7 +2102,7 @@ def create_py_executor_instance(
ctx_chunk_config=ctx_chunk_config,
peft_cache_manager=peft_cache_manager.impl
if peft_cache_manager is not None else None,
scheduler_capacity=scheduler_capacity,
scheduler_capacity=v2_scheduler_capacity,
draft_kv_cache_manager=draft_kv_cache_manager,
cross_kv_cache_manager=cross_kv_cache_manager,
no_schedule_until_state=no_schedule_until_state,
Expand Down
Loading
Loading