[Fix] Respect cache_protected_len in ChunkCache and disabled-radix release paths (#31662)
This commit is contained in:
@@ -80,8 +80,9 @@ class ChunkCache(BasePrefixCache):
|
|||||||
self, req: Req, is_insert: bool = True, *, kv_len_to_handle: int
|
self, req: Req, is_insert: bool = True, *, kv_len_to_handle: int
|
||||||
):
|
):
|
||||||
# For decode server: if req.output_ids is empty, we want to free all req.origin_input_ids
|
# For decode server: if req.output_ids is empty, we want to free all req.origin_input_ids
|
||||||
|
# The protected prefix is not this req's to free.
|
||||||
kv_indices = self.req_to_token_pool.req_to_token[
|
kv_indices = self.req_to_token_pool.req_to_token[
|
||||||
req.req_pool_idx, :kv_len_to_handle
|
req.req_pool_idx, req.cache_protected_len : kv_len_to_handle
|
||||||
]
|
]
|
||||||
self.token_to_kv_pool_allocator.free(kv_indices)
|
self.token_to_kv_pool_allocator.free(kv_indices)
|
||||||
|
|
||||||
@@ -146,9 +147,10 @@ class PureSWAChunkCache(SWAChunkCache):
|
|||||||
explicitly skip the range already freed by ``free_swa_out_of_window_slots``
|
explicitly skip the range already freed by ``free_swa_out_of_window_slots``
|
||||||
(a.k.a. _evict_swa) during decode.
|
(a.k.a. _evict_swa) during decode.
|
||||||
|
|
||||||
``req.swa_evict_floor`` only protects the prompt/image KV while the request
|
``req.swa_evict_floor`` shields the prompt/image KV from window eviction
|
||||||
is active. ChunkCache does not retain finished prefixes, so the protected
|
only while the request is active, so that range IS released here on
|
||||||
prefix is released here when the request finishes.
|
finish. Distinct from the ``cache_protected_len`` prefix, which is owned
|
||||||
|
elsewhere and never freed by this path.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def cache_finished_req(
|
def cache_finished_req(
|
||||||
@@ -158,15 +160,19 @@ class PureSWAChunkCache(SWAChunkCache):
|
|||||||
kv_indices = self.req_to_token_pool.req_to_token[
|
kv_indices = self.req_to_token_pool.req_to_token[
|
||||||
req.req_pool_idx, :kv_committed_len
|
req.req_pool_idx, :kv_committed_len
|
||||||
]
|
]
|
||||||
|
# The cache_protected_len prefix is not this req's to free.
|
||||||
|
protected_len = req.cache_protected_len
|
||||||
evict_floor = req.swa_evict_floor
|
evict_floor = req.swa_evict_floor
|
||||||
evicted_seqlen = req.kv.swa_evicted_seqlen
|
evicted_seqlen = req.kv.swa_evicted_seqlen
|
||||||
if evicted_seqlen > evict_floor:
|
if evicted_seqlen > evict_floor:
|
||||||
parts = []
|
parts = []
|
||||||
if evict_floor > 0:
|
if evict_floor > protected_len:
|
||||||
parts.append(kv_indices[:evict_floor])
|
parts.append(kv_indices[protected_len:evict_floor])
|
||||||
if evicted_seqlen < kv_committed_len:
|
if evicted_seqlen < kv_committed_len:
|
||||||
parts.append(kv_indices[evicted_seqlen:kv_committed_len])
|
parts.append(
|
||||||
|
kv_indices[max(evicted_seqlen, protected_len) : kv_committed_len]
|
||||||
|
)
|
||||||
if parts:
|
if parts:
|
||||||
self.token_to_kv_pool_allocator.free(torch.cat(parts))
|
self.token_to_kv_pool_allocator.free(torch.cat(parts))
|
||||||
else:
|
else:
|
||||||
self.token_to_kv_pool_allocator.free(kv_indices)
|
self.token_to_kv_pool_allocator.free(kv_indices[protected_len:])
|
||||||
|
|||||||
@@ -443,8 +443,9 @@ class RadixCache(SessionRadixCacheMixin, KVCacheEventMixin, BasePrefixCache):
|
|||||||
is_insert = False
|
is_insert = False
|
||||||
|
|
||||||
if self.disable:
|
if self.disable:
|
||||||
|
# The protected prefix is not this req's to free.
|
||||||
kv_indices = self.req_to_token_pool.req_to_token[
|
kv_indices = self.req_to_token_pool.req_to_token[
|
||||||
req.req_pool_idx, :kv_len_to_handle
|
req.req_pool_idx, req.cache_protected_len : kv_len_to_handle
|
||||||
]
|
]
|
||||||
self.token_to_kv_pool_allocator.free(kv_indices)
|
self.token_to_kv_pool_allocator.free(kv_indices)
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ class _FakeAllocator:
|
|||||||
class _FakeReq:
|
class _FakeReq:
|
||||||
req_pool_idx = 0
|
req_pool_idx = 0
|
||||||
swa_evict_floor = 3
|
swa_evict_floor = 3
|
||||||
|
cache_protected_len = 0
|
||||||
kv = SimpleNamespace(swa_evicted_seqlen=6)
|
kv = SimpleNamespace(swa_evicted_seqlen=6)
|
||||||
|
|
||||||
def pop_committed_kv_cache(self):
|
def pop_committed_kv_cache(self):
|
||||||
@@ -30,12 +31,16 @@ class _FakeReq:
|
|||||||
|
|
||||||
|
|
||||||
class TestPureSWAChunkCache(CustomTestCase):
|
class TestPureSWAChunkCache(CustomTestCase):
|
||||||
def test_finished_req_skips_already_evicted_swa_range(self):
|
def _make_cache(self):
|
||||||
cache = PureSWAChunkCache.__new__(PureSWAChunkCache)
|
cache = PureSWAChunkCache.__new__(PureSWAChunkCache)
|
||||||
cache.req_to_token_pool = SimpleNamespace(
|
cache.req_to_token_pool = SimpleNamespace(
|
||||||
req_to_token=torch.arange(10, dtype=torch.int64).unsqueeze(0)
|
req_to_token=torch.arange(10, dtype=torch.int64).unsqueeze(0)
|
||||||
)
|
)
|
||||||
cache.token_to_kv_pool_allocator = _FakeAllocator()
|
cache.token_to_kv_pool_allocator = _FakeAllocator()
|
||||||
|
return cache
|
||||||
|
|
||||||
|
def test_finished_req_skips_already_evicted_swa_range(self):
|
||||||
|
cache = self._make_cache()
|
||||||
|
|
||||||
cache.cache_finished_req(_FakeReq(), kv_len_to_handle=8)
|
cache.cache_finished_req(_FakeReq(), kv_len_to_handle=8)
|
||||||
|
|
||||||
@@ -43,6 +48,16 @@ class TestPureSWAChunkCache(CustomTestCase):
|
|||||||
freed = cache.token_to_kv_pool_allocator.freed[0]
|
freed = cache.token_to_kv_pool_allocator.freed[0]
|
||||||
self.assertTrue(torch.equal(freed, torch.tensor([0, 1, 2, 6, 7])))
|
self.assertTrue(torch.equal(freed, torch.tensor([0, 1, 2, 6, 7])))
|
||||||
|
|
||||||
|
def test_finished_req_skips_protected_prefix(self):
|
||||||
|
cache = self._make_cache()
|
||||||
|
req = _FakeReq()
|
||||||
|
req.cache_protected_len = 2
|
||||||
|
|
||||||
|
cache.cache_finished_req(req, kv_len_to_handle=8)
|
||||||
|
|
||||||
|
freed = cache.token_to_kv_pool_allocator.freed[0]
|
||||||
|
self.assertTrue(torch.equal(freed, torch.tensor([2, 6, 7])))
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|||||||
Reference in New Issue
Block a user