Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 13 additions & 9 deletions python/sglang/srt/managers/schedule_batch.py
Original file line number Diff line number Diff line change
Expand Up @@ -2920,7 +2920,18 @@ def _force_track_h(i: int) -> int:
# to force the math calculation to retrieve the correct mamba state from h.
return i + 1

mask = req.extend_range.length >= checkpoint_grid
# Pick the depth on the absolute checkpoint grid: a chunk boundary can leave
# the prefix off the (DCP-widened) tree page, and a prefix-relative depth then
# names a position no page can hold. Donate only where an h snapshot exists.
prefix_len = len(req.prefix_indices)
seq_end = prefix_len + req.extend_range.length
# mamba_track_seqlen_aligned/mamba_last_track_seqlen is actual tracked seqlen. Used to pass to
# mamba radix cache to track which seqlen this mamba state should store at.
mamba_track_seqlen_aligned = (seq_end // checkpoint_grid) * checkpoint_grid
mask = (
mamba_track_seqlen_aligned > prefix_len
and (mamba_track_seqlen_aligned - prefix_len) % cache_chunk_size == 0
)
track_index = req.kv.mamba_ping_pong_track_buffer[
req.kv.mamba_next_track_idx
].item()
Expand All @@ -2933,14 +2944,7 @@ def _force_track_h(i: int) -> int:
# otherwise retrieved from h (i.e. unaligned).
# We need to pass the non-aligned seqlen to the calculation. Even though
# we pass in mamba_track_seqlen, the actual tracked seqlen is mamba_last_track_seqlen.
mamba_track_seqlen = len(req.prefix_indices) + req.extend_range.length

# mamba_track_seqlen_aligned/mamba_last_track_seqlen is actual tracked seqlen. Used to pass to
# mamba radix cache to track which seqlen this mamba state should store at.
mamba_track_seqlen_aligned = (
len(req.prefix_indices)
+ (req.extend_range.length // checkpoint_grid) * checkpoint_grid
)
mamba_track_seqlen = seq_end

# A coarser checkpoint grid may not be a model-state boundary, so
# force retrieval from the intermediate h state in that case.
Expand Down
34 changes: 30 additions & 4 deletions test/registered/unit/managers/test_mamba_checkpoint_depth.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@
DCP widens the tree page past the mamba chunk grid. A checkpoint picked on the
finer grid names a depth no radix node can carry, so it gets attached to the
preceding node and a later request resumes from a state that already covers
tokens past that node.
tokens past that node. The same happens when the depth is measured from a
prefix that a chunked prefill left on the scheduler page but off the widened
tree page: 64 + 512 = 576 is no 512-page boundary, and the finished request
then frees two kv-row segments that share the page holding 576.
"""

import unittest
Expand All @@ -27,8 +30,9 @@
CHUNK = 64


def _track_seqlen(*, tree_page: int, prefix_len: int, extend_len: int) -> int:
"""Run one extend through the tracker and report the donated depth."""
def _track_seqlen(*, tree_page: int, prefix_len: int, extend_len: int) -> int | None:
"""Run one extend through the tracker and report the donated depth, or
None when the extend donates no checkpoint."""
server_args = ServerArgs(model_path="dummy", page_size=CHUNK)
# The property would otherwise load the HF config for the dummy model.
server_args._mamba_cache_chunk_size = CHUNK
Expand Down Expand Up @@ -57,7 +61,10 @@ def _track_seqlen(*, tree_page: int, prefix_len: int, extend_len: int) -> int:
batch.req_to_token_pool = MagicMock()
batch.req_to_token_pool.get_mamba_ping_pong_other_idx.return_value = 1

batch._mamba_radix_cache_v2_req_prepare_for_extend(req)
entry = batch._mamba_radix_cache_v2_req_prepare_for_extend(req)
if not entry.track_mask:
assert req.kv.mamba_last_track_seqlen is None
return None
return req.kv.mamba_last_track_seqlen


Expand All @@ -73,6 +80,25 @@ def test_unwidened_tree_page_keeps_the_chunk_grid(self):
depth = _track_seqlen(tree_page=CHUNK, prefix_len=16384, extend_len=4066)
self.assertEqual(depth, 20416)

def test_depth_is_picked_on_the_absolute_grid(self):
# (tree_page, prefix_len, extend_len) -> donated depth, or None when the
# extend crosses no page or the prefix is off the kernel chunk grid.
for tree_page, prefix_len, extend_len, expected in (
(512, 64, 540, 512),
(512, 448, 128, 512),
(512, 64, 400, None),
(512, 37, 540, None),
):
with self.subTest(prefix_len=prefix_len, extend_len=extend_len):
self.assertEqual(
_track_seqlen(
tree_page=tree_page,
prefix_len=prefix_len,
extend_len=extend_len,
),
expected,
)


class TestMambaTrackGrid(unittest.TestCase):
def _grid(self, *, interval: int, tree_page: int, chunk: int = CHUNK) -> int:
Expand Down
Loading