From 1adb53f1478cc8b76df505bdd3e22aa6fd7b4d78 Mon Sep 17 00:00:00 2001 From: shihaozhou <165879644+shihaoustc@users.noreply.github.com> Date: Mon, 22 Jun 2026 20:47:29 +0800 Subject: [PATCH] Fix CP page filtering by request-local position (#28718) --- .../sglang/srt/disaggregation/common/conn.py | 1 + python/sglang/srt/disaggregation/utils.py | 54 +++++++++++-------- 2 files changed, 32 insertions(+), 23 deletions(-) diff --git a/python/sglang/srt/disaggregation/common/conn.py b/python/sglang/srt/disaggregation/common/conn.py index 67115dfd2..26ba9c9a0 100644 --- a/python/sglang/srt/disaggregation/common/conn.py +++ b/python/sglang/srt/disaggregation/common/conn.py @@ -867,6 +867,7 @@ class CommonKVSender(BaseKVSender): self.kv_mgr, kv_indices, index_slice, + total_pages=self.num_kv_indices, ) elif self.kv_mgr.is_dummy_cp_rank: if not is_last_chunk: diff --git a/python/sglang/srt/disaggregation/utils.py b/python/sglang/srt/disaggregation/utils.py index 84bce7262..c09c0fb17 100644 --- a/python/sglang/srt/disaggregation/utils.py +++ b/python/sglang/srt/disaggregation/utils.py @@ -511,6 +511,16 @@ def get_kv_class( raise ValueError(f"Unsupported transfer backend: {transfer_backend}") +def _get_cp_rank_page_bounds( + total_pages: int, cp_rank: int, cp_size: int +) -> Tuple[int, int]: + base = total_pages // cp_size + rem = total_pages % cp_size + local_start = cp_rank * base + min(cp_rank, rem) + n_pages = base + (1 if cp_rank < rem else 0) + return local_start, local_start + n_pages + + def page_indices_to_cp_rank_page_indices( page_indices: np.ndarray, total_pages: int, @@ -558,37 +568,35 @@ def page_indices_to_cp_rank_page_indices( def filter_kv_indices_for_cp_rank( - kv_mgr: CommonKVManager, kv_indices: np.ndarray, index_slice: slice + kv_mgr: CommonKVManager, + kv_indices: np.ndarray, + index_slice: slice, + total_pages: Optional[int] = None, ) -> Tuple[np.ndarray, slice]: """Filters kv_indices and index_slice for the current CP rank.""" - total_pages = len(kv_indices) + if total_pages is None: + total_pages = len(kv_indices) cp_rank = kv_mgr.attn_cp_rank cp_size = kv_mgr.attn_cp_size - rank_page_indices = page_indices_to_cp_rank_page_indices( - page_indices=kv_indices, - total_pages=total_pages, - cp_rank=cp_rank, - cp_size=cp_size, - ) + if cp_size <= 1: + return kv_indices, index_slice - if rank_page_indices.size == 0: + rank_start, rank_end = _get_cp_rank_page_bounds(total_pages, cp_rank, cp_size) + chunk_start = index_slice.start if index_slice.start is not None else 0 + chunk_end = index_slice.stop if index_slice.stop is not None else total_pages + first_pos = max(rank_start, chunk_start) - chunk_start + last_pos = min(rank_end, chunk_end) - chunk_start + + if last_pos <= first_pos: new_kv_indices = kv_indices[:0] - new_index_slice = slice(index_slice.start, index_slice.start) + new_index_slice = slice(chunk_start, chunk_start) else: - mask = np.isin(kv_indices, rank_page_indices) - if not mask.any(): - new_kv_indices = kv_indices[:0] - new_index_slice = slice(index_slice.start, index_slice.start) - else: - first_pos = int(mask.argmax()) - last_pos = len(mask) - int(mask[::-1].argmax()) - - new_kv_indices = kv_indices[first_pos:last_pos] - new_index_slice = slice( - index_slice.start + first_pos, - index_slice.start + last_pos, - ) + new_kv_indices = kv_indices[first_pos:last_pos] + new_index_slice = slice( + chunk_start + first_pos, + chunk_start + last_pos, + ) return new_kv_indices, new_index_slice