[Fix] Forward SWA prealloc reclaim through the DSV4 HiSparse allocator (#40354)

Co-authored-by: Mohammad Angkad <mohammad.angkad@radixark.ai>
This commit is contained in:
Mohammad Miadh Angkad
2026-09-19 20:27:51 -07:00
committed by GitHub
co-authored by Mohammad Angkad
parent 020703923d
commit df0dc44931
2 changed files with 27 additions and 0 deletions
@@ -376,6 +376,14 @@ class DeepSeekV4HiSparseTokenToKVPoolAllocator(BaseTokenToKVPoolAllocator):
def swa_available_size(self):
return self.logical_attn_allocator.swa_available_size()
def reclaim_for_prealloc(
self, tree_cache, full_tokens: int, swa_tokens: int
) -> str | None:
# C4 needs no reclaim here: full_available_size prices it into the budget.
return self.logical_attn_allocator.reclaim_for_prealloc(
tree_cache, full_tokens, swa_tokens
)
def free_swa(self, free_indices: torch.Tensor):
self.logical_attn_allocator.free_swa(free_indices)
@@ -60,6 +60,25 @@ class TestDeepSeekV4HiSparseAllocator(CustomTestCase):
self.assertEqual(kwargs["extend_num_tokens"], 512)
self.assertEqual(kwargs["swa_tail_len"], 128)
def test_forwards_prealloc_reclaim_to_logical_allocator(self):
"""PD decode preallocation must not crash on the HiSparse composite."""
allocator = object.__new__(DeepSeekV4HiSparseTokenToKVPoolAllocator)
logical_allocator = MagicMock(spec=["reclaim_for_prealloc"])
allocator.logical_attn_allocator = logical_allocator
logical_allocator.reclaim_for_prealloc.return_value = None
tree_cache = object()
self.assertIsNone(allocator.reclaim_for_prealloc(tree_cache, 512, 256))
logical_allocator.reclaim_for_prealloc.assert_called_once_with(
tree_cache, 512, 256
)
logical_allocator.reclaim_for_prealloc.return_value = "SWA eviction short"
self.assertEqual(
allocator.reclaim_for_prealloc(tree_cache, 512, 256),
"SWA eviction short",
)
def test_hisparse_budget_uses_full_logical_capacity_for_swa_tail(self):
from sglang.srt.disaggregation.decode import DecodePreallocQueue