dsv4.1: compression, KV I/O, and metadata kernels (#39652)

Co-authored-by: BBuf <1182563586@qq.com>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-authored-by: DarkSharpness <76582120+DarkSharpness@users.noreply.github.com>
This commit is contained in:
Liangsheng Yin
2026-09-16 13:54:07 -07:00
committed by GitHub
co-authored by BBuf Claude Opus 5 DarkSharpness
parent 869674b3a7
commit 13d593b6cf
19 changed files with 2102 additions and 279 deletions
@@ -0,0 +1,72 @@
"""Boundary tests for the packed indices in the DSV4 prefill write plan."""
from __future__ import annotations
import unittest
import torch
from sglang.test.ci.ci_register import register_cuda_ci
from sglang.test.kernels.deepseek_v4.common import (
make_legacy_context,
make_paged_context,
to_seq_extend,
)
from sglang.test.test_utils import CustomTestCase
register_cuda_ci(est_time=20, stage="base-b-kernel-unit", runner_config="1-gpu-large")
class TestCompressWritePlanBounds(CustomTestCase):
def test_64k_prefill_preserves_last_token(self):
"""65536 tokens fit uint16 indices; the last token must not wrap or vanish."""
for cr in (4, 128):
paged = make_paged_context(
bs=16, compress_ratio=cr, num_swa_pages_per_req=16
)
legacy = make_legacy_context(bs=16, compress_ratio=cr)
seq_lens, extend_lens, num_q = to_seq_extend([(4096, 4096)] * 16)
for ctx, on_gpu in ((paged, False), (paged, True), (legacy, False)):
with self.subTest(cr=cr, paged=ctx is paged, on_gpu=on_gpu):
device = "cuda" if on_gpu else "cpu"
plan = ctx.make_prefill_plan(
seq_lens.to(device), extend_lens.to(device), num_q
)
c = plan.plan_c.cpu().view(torch.int32).view(-1, 4)
valid_c = c[:, 0] != -1
ids = c[valid_c, 1].bitwise_and(0xFFFF).sort().values
torch.testing.assert_close(
ids, torch.arange(cr - 1, num_q, cr, dtype=torch.int32)
)
w = plan.plan_w.cpu().view(torch.int32).view(-1, 2)
last = w[w[:, 0] == 65535]
if cr == 4:
self.assertEqual(len(last), 1)
self.assertEqual(int(last[0, 1]), ctx.state_loc(15, 4095))
else:
# Non-overlapping C128 consumed the complete final block;
# no raw tail remains to persist into the state ring.
self.assertEqual(len(w[w[:, 0] != -1]), 0)
def test_prefill_rejects_uint16_index_overflow(self):
for ctx in (
make_paged_context(bs=16, compress_ratio=4, num_swa_pages_per_req=17),
make_legacy_context(bs=16, compress_ratio=4),
):
seq_lens, extend_lens, num_q = to_seq_extend(
[(4096, 4096)] * 15 + [(4097, 4097)]
)
with self.assertRaisesRegex(RuntimeError, "plan_compress_prefill"):
ctx.make_prefill_plan(seq_lens, extend_lens, num_q)
def test_prefill_rejects_packed_invalid_sentinel(self):
# A 65536-request, one-token-per-request batch makes the last packed
# (batch_id, ragged_id) equal (65535, 65535), the invalid write sentinel.
ctx = make_legacy_context(bs=65536, compress_ratio=4)
seq_lens, extend_lens, num_q = to_seq_extend([(1, 1)] * 65536)
with self.assertRaisesRegex(RuntimeError, "plan_compress_prefill"):
ctx.make_prefill_plan(seq_lens, extend_lens, num_q)
if __name__ == "__main__":
unittest.main()
@@ -1,217 +0,0 @@
"""Kernel-level tests for the DSV4 compress write-plan (`plan_prefill`).
`plan_w` decides which tokens' raw KV get persisted into the compress-state ring
for a *future* compression window to read. A speculative verify batch plans from
the optimistic `seq_len = prefix + num_draft_tokens` but rolls back to
`prefix + accept_len`, so every committed token must stay resident whatever the
accept length -- i.e. the plan must write all of `[prefix, seq_len)`.
`c_plan.cuh` used to cap that pad at 4 (`kMaxMTPDraftTokens`), silently
under-writing the ring for larger draft counts -- no IMA, no NaN, just wrong
compressed state. The pad now comes from the ring itself
(`ring_size - window_size + 2`), covering every draft count the ring can serve.
Tests pin the invariant on both planner paths (CPU host loop and GPU
`plan_compress_prefill_kernel0`) and both compress ratios.
"""
from __future__ import annotations
import unittest
import torch
from sglang.test.ci.ci_register import register_cuda_ci
from sglang.test.kernels.deepseek_v4.common import make_paged_context, to_seq_extend
from sglang.test.test_utils import CustomTestCase
register_cuda_ci(est_time=20, stage="base-b-kernel-unit", runner_config="1-gpu-large")
C4_RING_SIZE = 16 # get_compress_state_ring_size(4, is_speculative=True)
C128_RING_SIZE = 256 # get_compress_state_ring_size(128, is_speculative=True)
C4_RING_SIZE_NO_SPEC = 8 # get_compress_state_ring_size(4, is_speculative=False)
C128_RING_SIZE_NO_SPEC = 128 # get_compress_state_ring_size(128, is_speculative=False)
def _window_size(compress_ratio: int) -> int:
"""Tokens read by one compression: c4 overlaps two chunks, c128 does not."""
return compress_ratio * (2 if compress_ratio == 4 else 1)
def _max_draft_tokens(compress_ratio: int, ring_size: int) -> int:
"""Largest draft count this ring serves; mirrors `mtp_pad` in c_plan.cuh."""
window = _window_size(compress_ratio)
return ring_size - window + 2 if ring_size > window else 0
def _written_positions(plan_w: torch.Tensor, prefix_len: int) -> set[int]:
"""Decode `plan_w` into the set of positions written, for a bs=1 plan.
`plan_w` is `[n, 8]` uint8 = (uint32 ragged_id, int32 write_loc). Stage 1
overwrites `write_loc` with the final state slot, but `ragged_id` survives and
equals the token's index within the ragged layout, so for a single request
`position = prefix_len + ragged_id`.
"""
words = plan_w.cpu().view(torch.uint32).view(-1, 2)
ragged_ids = words[:, 0]
valid = ragged_ids != 0xFFFFFFFF
return {prefix_len + int(r) for r in ragged_ids[valid]}
class TestCompressWritePlanDraftPad(CustomTestCase):
def _make_plan_positions(
self,
*,
compress_ratio: int,
ring_size: int,
prefix_len: int,
num_draft_tokens: int,
on_gpu: bool = False,
) -> set[int]:
"""Build a bs=1 verify plan and return the positions it writes.
`on_gpu=True` moves the planner inputs to device, which routes
`plan_prefill` to `plan_compress_prefill_kernel0` instead of the host loop.
"""
ctx = make_paged_context(
bs=1, compress_ratio=compress_ratio, ring_size=ring_size
)
seq_lens, extend_lens, num_q = to_seq_extend(
[(prefix_len + num_draft_tokens, num_draft_tokens)]
)
if on_gpu:
seq_lens = seq_lens.to(ctx.req_to_token.device)
extend_lens = extend_lens.to(ctx.req_to_token.device)
plan = ctx.make_prefill_plan(seq_lens, extend_lens, num_q)
return _written_positions(plan.plan_w, prefix_len)
def _assert_ring_residency(self, compress_ratio: int, ring_size: int):
"""Every committed token must be written, for each (D, sl mod cr) combo.
This is the sufficient condition, which is why there is no multi-step replay
test: if a step writes all of `[prefix, prefix + D)`, then whatever the accept
length, the tokens the next compression window needs are either from this step
(written here) or older (written by an earlier step, same invariant by
induction).
"""
max_d = _max_draft_tokens(compress_ratio, ring_size)
# Vary `seq_len % compress_ratio`: that residue decides whether the unpadded rule
# alone would have sufficed. Four is enough -- with the pad in place it dominates
# `last_c_pos` for every residue, so the rest repeat one branch. Bases are
# page-aligned so the swa-page-boundary clause does not mask the pad.
bases = [512 + off for off in range(min(compress_ratio, 4))]
draft_counts = sorted(
{1, 2, 3, 4, 5, max_d - 1, max_d} & set(range(1, max_d + 1))
)
for num_draft_tokens in draft_counts:
for prefix_len in bases:
with self.subTest(
cr=compress_ratio,
D=num_draft_tokens,
prefix=prefix_len,
):
written = self._make_plan_positions(
compress_ratio=compress_ratio,
ring_size=ring_size,
prefix_len=prefix_len,
num_draft_tokens=num_draft_tokens,
)
seq_len = prefix_len + num_draft_tokens
missing = set(range(prefix_len, seq_len)) - written
self.assertEqual(
missing,
set(),
f"plan_w skipped committed positions {sorted(missing)}; "
f"a later compression would read stale ring slots",
)
def test_c4_ring_residency(self):
self._assert_ring_residency(4, C4_RING_SIZE)
def test_c128_ring_residency(self):
self._assert_ring_residency(128, C128_RING_SIZE)
def test_cpu_and_gpu_planner_agree(self):
"""Both planner paths must emit the same write set.
The residency invariants above are checked on the host-loop plan; this
pins the GPU `plan_compress_prefill_kernel0` plan to it, so the pad fix
has to hold on both paths.
"""
for compress_ratio, ring_size in ((4, C4_RING_SIZE), (128, C128_RING_SIZE)):
max_d = _max_draft_tokens(compress_ratio, ring_size)
for num_draft_tokens in (1, 4, max_d):
for prefix_len in (512, 513, 515):
with self.subTest(
cr=compress_ratio, D=num_draft_tokens, prefix=prefix_len
):
kwargs = dict(
compress_ratio=compress_ratio,
ring_size=ring_size,
prefix_len=prefix_len,
num_draft_tokens=num_draft_tokens,
)
self.assertEqual(
self._make_plan_positions(**kwargs, on_gpu=False),
self._make_plan_positions(**kwargs, on_gpu=True),
)
def test_plain_prefill_write_set(self):
"""A non-speculative ring is exactly one window wide, so the pad is 0 and the
base write rule stands unchanged for both ratios."""
for compress_ratio, ring_size in (
(4, C4_RING_SIZE_NO_SPEC),
(128, C128_RING_SIZE_NO_SPEC),
):
self.assertEqual(_max_draft_tokens(compress_ratio, ring_size), 0)
is_overlap = compress_ratio == 4
for seq_len in (512, 600, 777):
with self.subTest(cr=compress_ratio, sl=seq_len):
ctx = make_paged_context(
bs=1, compress_ratio=compress_ratio, ring_size=ring_size
)
seq_lens, extend_lens, num_q = to_seq_extend([(seq_len, seq_len)])
plan = ctx.make_prefill_plan(seq_lens, extend_lens, num_q)
written = _written_positions(plan.plan_w, 0)
last_c_pos = seq_len // compress_ratio * compress_ratio
first_w_pos = last_c_pos - (compress_ratio if is_overlap else 0)
sps = ctx.swa_page_size
expected = {
p
for p in range(seq_len)
if p >= first_w_pos
or (is_overlap and p % sps >= sps - compress_ratio)
}
self.assertEqual(written, expected)
def test_over_capacity_under_writes(self):
"""Beyond the ring's capacity the plan silently under-writes.
The planner cannot tell an over-configured verify batch from an ordinary long
prefill, so it cannot fail loudly -- hence the startup check in
`DSV4PoolConfigurator._assert_ring_serves_draft_tokens`.
"""
for compress_ratio, ring_size in ((4, C4_RING_SIZE), (128, C128_RING_SIZE)):
max_d = _max_draft_tokens(compress_ratio, ring_size)
# Far enough over that the `last_c_pos` term cannot cover the gap for any
# residue of `seq_len % compress_ratio`.
too_many = max_d + compress_ratio + 1
prefix_len = 512
with self.subTest(cr=compress_ratio, D=too_many):
written = self._make_plan_positions(
compress_ratio=compress_ratio,
ring_size=ring_size,
prefix_len=prefix_len,
num_draft_tokens=too_many,
)
missing = set(range(prefix_len, prefix_len + too_many)) - written
self.assertNotEqual(
missing,
set(),
"expected the plan to under-write past the ring capacity; if this "
"now covers everything, the startup bound can be relaxed",
)
if __name__ == "__main__":
unittest.main()