[Bugfix][Core] Fix host memory leak from undrained new_block_ids (#44490)

Signed-off-by: Ting Sun <suntcrick@gmail.com>
Signed-off-by: Nick Hill <nickhill123@gmail.com>
Co-authored-by: Nick Hill <nickhill123@gmail.com>
Co-authored-by: chaunceyjiang <chaunceyjiang@gmail.com>
This commit is contained in:
Ting SUN
2026-07-07 07:32:55 +00:00
committed by GitHub
co-authored by Nick Hill chaunceyjiang
parent 1e823dc01d
commit b4cfbc24d3
3 changed files with 18 additions and 15 deletions
+1
View File
@@ -115,6 +115,7 @@ class KVCacheCoordinator(ABC):
dcp_world_size=dcp_world_size,
pcp_world_size=pcp_world_size,
scheduler_block_size=self.scheduler_block_size,
needs_kv_cache_zeroing=self.kv_cache_config.needs_kv_cache_zeroing,
)
for i, kv_cache_group in enumerate(self.kv_cache_config.kv_cache_groups)
)
+4 -3
View File
@@ -1078,10 +1078,11 @@ class Scheduler(SchedulerInterface):
self.prev_step_scheduled_req_ids.clear()
self.prev_step_scheduled_req_ids.update(num_scheduled_tokens.keys())
# Drain new attention block ids every step so the manager-side list
# does not grow unbounded; only kv-cache zeroing consumes them.
new_attn_block_ids = self.kv_cache_manager.take_new_block_ids()
new_block_ids_to_zero = (
(self.kv_cache_manager.take_new_block_ids() or None)
if self.needs_kv_cache_zeroing
else None
(new_attn_block_ids or None) if self.needs_kv_cache_zeroing else None
)
# Dynamic speculative decoding: compute optimal K
+13 -12
View File
@@ -45,6 +45,7 @@ class SingleTypeKVCacheManager(ABC):
scheduler_block_size: int,
dcp_world_size: int = 1,
pcp_world_size: int = 1,
needs_kv_cache_zeroing: bool = False,
max_admission_blocks_per_request: int | None = None,
) -> None:
"""
@@ -55,6 +56,8 @@ class SingleTypeKVCacheManager(ABC):
kv_cache_group_id: The id of the kv cache group of this manager.
scheduler_block_size: The scheduling granularity (LCM of all group
block sizes); a multiple of this manager's ``block_size``.
needs_kv_cache_zeroing: Whether worker-side KV cache zeroing needs
newly allocated block IDs from this manager.
max_admission_blocks_per_request: Recycling-aware per-request
block cap used by `get_num_blocks_to_allocate`. Only set for
spec types that recycle blocks across chunks (SWA,
@@ -73,6 +76,14 @@ class SingleTypeKVCacheManager(ABC):
self.block_pool = block_pool
self.enable_caching = enable_caching
self._max_admission_blocks_per_request = max_admission_blocks_per_request
# Record newly allocated block ids only when worker-side zeroing will
# consume them and this manager holds a spec type that gets zeroed.
self._record_new_block_ids = needs_kv_cache_zeroing and type(kv_cache_spec) in (
FullAttentionSpec,
TQFullAttentionSpec,
MLAAttentionSpec,
HiddenStateCacheSpec,
)
self.new_block_ids: list[int] = []
# Mapping from request ID to blocks to track the blocks allocated
@@ -268,12 +279,7 @@ class SingleTypeKVCacheManager(ABC):
cdiv(num_total_computed_tokens, self.block_size) - len(req_blocks)
)
req_blocks.extend(allocated_blocks)
if type(self.kv_cache_spec) in (
FullAttentionSpec,
TQFullAttentionSpec,
MLAAttentionSpec,
HiddenStateCacheSpec,
):
if self._record_new_block_ids:
self.new_block_ids.extend(b.block_id for b in allocated_blocks)
def allocate_new_blocks(
@@ -301,12 +307,7 @@ class SingleTypeKVCacheManager(ABC):
else:
new_blocks = self.block_pool.get_new_blocks(num_new_blocks)
req_blocks.extend(new_blocks)
if type(self.kv_cache_spec) in (
FullAttentionSpec,
TQFullAttentionSpec,
MLAAttentionSpec,
HiddenStateCacheSpec,
):
if self._record_new_block_ids:
self.new_block_ids.extend(b.block_id for b in new_blocks)
return new_blocks