dsv4.1: remaining model and runtime integration (#38798)

Co-authored-by: BBuf <1182563586@qq.com>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-authored-by: Xiaoyu Zhang <xiaoyu.zhang@radixark.ai>
Co-authored-by: Yuwei An <ayw.sirius19@gmail.com>
Co-authored-by: Khoa Pham <khoa.pham@radixark.ai>
Co-authored-by: Yuhao Yang <47235274+yhyang201@users.noreply.github.com>
Co-authored-by: Zhichen Zeng <zczeng@uw.edu>
Co-authored-by: Ke Bao <ispobaoke@gmail.com>
This commit is contained in:
Liangsheng Yin
2026-09-18 02:55:30 -07:00
committed by GitHub
co-authored by BBuf Claude Opus 5 Xiaoyu Zhang Yuwei An Khoa Pham Yuhao Yang Zhichen Zeng Ke Bao
parent 1b200ffaaa
commit a6cf05817f
103 changed files with 8807 additions and 723 deletions
@@ -5,11 +5,17 @@ from unittest.mock import MagicMock, patch
import torch
from sglang.kernels.ops.attention.dsv4.kv_layout import (
KVLayout,
is_valid_kv_layout_pair,
)
from sglang.srt.mem_cache.allocator.swa import SWATokenToKVPoolAllocator
from sglang.srt.mem_cache.deepseek_v4_memory_pool import (
DeepSeekV4SingleKVPool,
DeepSeekV4TokenToKVPool,
_CompressedPoolConfig,
)
from sglang.srt.runtime_context import get_context
from sglang.test.ci.ci_register import register_cpu_ci
from sglang.test.test_utils import CustomTestCase
@@ -25,6 +31,8 @@ class TestDSV4CompressedPools(CustomTestCase):
pool = DeepSeekV4TokenToKVPool.__new__(DeepSeekV4TokenToKVPool)
pool._unified_kv = unified
pool.uniform_fp8 = False
pool.kv_layout = KVLayout.V4
pool.compressed_kv_layout_option = None
pool.compressed_pool_configs = {
4: _CompressedPoolConfig(
256, 64, torch.bfloat16, indexer_size=1024
@@ -176,5 +184,151 @@ class TestDSV4CompressedPools(CustomTestCase):
pool.get_index_k_page_size(128)
HEAD_DIM = 512
ROPE_DIM = 64
PAGE_SIZE = 256
FULL_SIZE = 4 * PAGE_SIZE
class TestV41KVPoolLayouts(CustomTestCase):
"""A V4.1-layout pool hands the attention kernel page-aligned buffers and
picks the compressed layout each ratio asks for."""
def setUp(self):
super().setUp()
override = get_context().override_server_args(page_size=PAGE_SIZE)
override.install()
self.addCleanup(override.restore)
def make_pool(self, ratios, kv_source_layers, kv_layout, compressed=None, **sizes):
return DeepSeekV4TokenToKVPool(
max_num_reqs=16,
swa_size=FULL_SIZE,
c4_size=sizes.get("c4_size", 0),
c128_size=sizes.get("c128_size", 0),
c4_state_pool_size=sizes.get("c4_state_pool_size", 0),
c128_state_pool_size=sizes.get("c128_state_pool_size", 0),
page_size=PAGE_SIZE,
swa_page_size=PAGE_SIZE,
dtype=torch.float8_e4m3fn,
c4_state_dtype=torch.float32,
c128_state_dtype=torch.float32,
qk_nope_head_dim=HEAD_DIM - ROPE_DIM,
qk_rope_head_dim=ROPE_DIM,
indexer_head_dim=128,
layer_num=len(ratios),
device="cpu",
enable_memory_saver=False,
compression_ratios=ratios,
kv_source_layers=kv_source_layers,
full_size=FULL_SIZE,
kv_layout=kv_layout,
compressed_kv_layout=compressed,
)
def assert_kernel_requirements(self, pool, layout):
"""Pages start on the kernel's alignment, and its
(num_pages, page_size, 1, bytes_per_token) view walks one token per row."""
for buf in pool.kv_buffer:
self.assertEqual(buf.stride(0) % layout.page_align, 0)
bpt = layout.bytes_per_token
view = buf[:, : pool.page_size * bpt].view(
buf.shape[0], pool.page_size, 1, bpt
)
self.assertEqual(view.stride(1), bpt)
self.assertEqual(view.stride(0), pool.bytes_per_page_padded)
def test_v41_pool_buffers(self):
for option, expect in ((None, KVLayout.V41_FP4), ("fp8", KVLayout.V41)):
with self.subTest(compressed=option):
pool = self.make_pool([0, 0, 2, 1, 1], [2, 3], KVLayout.V41, option)
self.assert_kernel_requirements(pool.swa_kv_pool, KVLayout.V41)
self.assertEqual(pool.get_swa_key_bytes_per_token(), 528)
for ratio in (1, 2):
layer_id = pool.sources_by_ratio[ratio][0]
self.assertIs(pool.get_extra_key_layout(layer_id), expect)
self.assertEqual(
pool.get_extra_key_bytes_per_token(layer_id),
expect.bytes_per_token,
)
self.assertTrue(is_valid_kv_layout_pair(pool.kv_layout, expect))
self.assert_kernel_requirements(pool.kv_pools[ratio], expect)
# A pool of the fp4 layout cannot be the main cache.
with self.assertRaises(AssertionError):
self.make_pool([0], [], KVLayout.V41_FP4)
def test_v41_pool_with_c4_c128(self):
pool = self.make_pool(
[0, 4, 128],
[],
KVLayout.V41,
c4_size=PAGE_SIZE,
c128_size=PAGE_SIZE,
c4_state_pool_size=16,
c128_state_pool_size=16,
)
for ratio in (4, 128):
self.assertEqual(pool.kv_pools[ratio].page_size, PAGE_SIZE // ratio)
# The 2-token c128 page is the only production page that pads.
self.assertEqual(pool.kv_pools[128].bytes_per_page_padded, 1536)
class TestPagedDSparkWithEncoderReplay(CustomTestCase):
def setUp(self):
super().setUp()
override = get_context().override_server_args(
enable_encoder_swa_bounded_replay=True,
speculative_algorithm="DSPARK",
speculative_num_draft_tokens=6,
speculative_dspark_block_size=5,
page_size=256,
max_running_requests=2,
chunked_prefill_size=256,
)
override.install()
self.addCleanup(override.restore)
def make_pool(self, *, draft):
return DeepSeekV4TokenToKVPool(
max_num_reqs=2,
num_req_slots=3,
swa_size=1024,
c4_size=0,
c128_size=0,
c4_state_pool_size=0,
c128_state_pool_size=0,
page_size=256,
swa_page_size=256,
dtype=torch.float8_e4m3fn,
c4_state_dtype=torch.float32,
c128_state_dtype=torch.bfloat16,
qk_nope_head_dim=448,
qk_rope_head_dim=64,
indexer_head_dim=128,
layer_num=3,
device="cpu",
enable_memory_saver=False,
compression_ratios=[0, 0, 0],
online_mtp_max_draft_tokens=6,
full_size=2048,
is_draft_worker=draft,
)
def test_target_window_and_draft_paged_storage_share_allocator_mapping(self):
target = self.make_pool(draft=False)
draft = self.make_pool(draft=True)
allocator = SWATokenToKVPoolAllocator(
2048, 1024, 256, torch.float8_e4m3fn, "cpu", target, False
)
draft.register_mapping(allocator.full_to_swa_index_mapping)
allocator.full_to_swa_index_mapping[256:512] = torch.arange(768, 1024)
self.assertEqual(
draft.translate_loc_from_full_to_swa(
torch.tensor([256, 300, 511])
).tolist(),
[768, 812, 1023],
)
if __name__ == "__main__":
unittest.main()
@@ -48,6 +48,7 @@ class TestUnifiedRadixHiCacheDispatch(unittest.TestCase):
)
kvcache = _mock_kvcache(DeepSeekV4TokenToKVPool)
kvcache.swa_kv_pool = MagicMock()
strategy = _select_strategy(kvcache, {FULL, SWA})
self.assertIsInstance(strategy, _DeepSeekV4Strategy)
@@ -141,6 +142,7 @@ class TestUnifiedRadixHiCacheDispatch(unittest.TestCase):
for cls in (SWAKVPool, DeepSeekV4TokenToKVPool):
kvcache = _mock_kvcache(cls)
kvcache.swa_kv_pool = MagicMock()
with self.assertRaises(AssertionError) as cm:
_select_strategy(kvcache, {FULL})
self.assertIn("No matching HiCache strategy", str(cm.exception))