[PP] bugfix: include CP size in PP rank offset (#29571)
This commit is contained in:
@@ -124,7 +124,9 @@ class SchedulerRequestReceiver:
|
|||||||
recv_reqs = None
|
recv_reqs = None
|
||||||
else:
|
else:
|
||||||
if self.ps.attn_tp_rank == 0 and self.ps.attn_cp_rank == 0:
|
if self.ps.attn_tp_rank == 0 and self.ps.attn_cp_rank == 0:
|
||||||
dp_offset = self.ps.attn_dp_rank * self.ps.attn_tp_size
|
dp_offset = (
|
||||||
|
self.ps.attn_dp_rank * self.ps.attn_cp_size * self.ps.attn_tp_size
|
||||||
|
)
|
||||||
recv_reqs = point_to_point_pyobj(
|
recv_reqs = point_to_point_pyobj(
|
||||||
[],
|
[],
|
||||||
self.ps.pp_rank * self.ps.tp_size + dp_offset,
|
self.ps.pp_rank * self.ps.tp_size + dp_offset,
|
||||||
|
|||||||
@@ -927,7 +927,9 @@ class SchedulerPPMixin:
|
|||||||
def _pp_send_pyobj_to_next_stage(self: Scheduler, data, async_send: bool = False):
|
def _pp_send_pyobj_to_next_stage(self: Scheduler, data, async_send: bool = False):
|
||||||
p2p_work = []
|
p2p_work = []
|
||||||
if self.ps.attn_tp_rank == 0 and self.ps.attn_cp_rank == 0:
|
if self.ps.attn_tp_rank == 0 and self.ps.attn_cp_rank == 0:
|
||||||
dp_offset = self.ps.attn_dp_rank * self.ps.attn_tp_size
|
dp_offset = (
|
||||||
|
self.ps.attn_dp_rank * self.ps.attn_cp_size * self.ps.attn_tp_size
|
||||||
|
)
|
||||||
p2p_work = point_to_point_pyobj(
|
p2p_work = point_to_point_pyobj(
|
||||||
data,
|
data,
|
||||||
self.ps.pp_rank * self.ps.tp_size + dp_offset,
|
self.ps.pp_rank * self.ps.tp_size + dp_offset,
|
||||||
@@ -940,7 +942,9 @@ class SchedulerPPMixin:
|
|||||||
|
|
||||||
def _pp_recv_pyobj_from_prev_stage(self: Scheduler):
|
def _pp_recv_pyobj_from_prev_stage(self: Scheduler):
|
||||||
if self.ps.attn_tp_rank == 0 and self.ps.attn_cp_rank == 0:
|
if self.ps.attn_tp_rank == 0 and self.ps.attn_cp_rank == 0:
|
||||||
dp_offset = self.ps.attn_dp_rank * self.ps.attn_tp_size
|
dp_offset = (
|
||||||
|
self.ps.attn_dp_rank * self.ps.attn_cp_size * self.ps.attn_tp_size
|
||||||
|
)
|
||||||
data = point_to_point_pyobj(
|
data = point_to_point_pyobj(
|
||||||
[],
|
[],
|
||||||
self.ps.pp_rank * self.ps.tp_size + dp_offset,
|
self.ps.pp_rank * self.ps.tp_size + dp_offset,
|
||||||
|
|||||||
@@ -0,0 +1,134 @@
|
|||||||
|
import unittest
|
||||||
|
from types import SimpleNamespace
|
||||||
|
from unittest.mock import patch
|
||||||
|
|
||||||
|
from sglang.test.ci.ci_register import register_cpu_ci
|
||||||
|
from sglang.test.test_utils import maybe_stub_sgl_kernel
|
||||||
|
|
||||||
|
maybe_stub_sgl_kernel()
|
||||||
|
|
||||||
|
from sglang.srt.distributed.parallel_state_wrapper import ParallelState # noqa: E402
|
||||||
|
from sglang.srt.managers.scheduler_components.request_receiver import ( # noqa: E402
|
||||||
|
SchedulerRequestReceiver,
|
||||||
|
)
|
||||||
|
from sglang.srt.managers.scheduler_pp_mixin import SchedulerPPMixin # noqa: E402
|
||||||
|
|
||||||
|
register_cpu_ci(est_time=2, suite="base-a-test-cpu")
|
||||||
|
|
||||||
|
|
||||||
|
def _make_ps(**overrides) -> ParallelState:
|
||||||
|
defaults = dict(
|
||||||
|
tp_rank=0,
|
||||||
|
tp_size=8,
|
||||||
|
pp_rank=1,
|
||||||
|
pp_size=2,
|
||||||
|
dp_rank=None,
|
||||||
|
dp_size=1,
|
||||||
|
attn_tp_rank=0,
|
||||||
|
attn_tp_size=2,
|
||||||
|
attn_cp_rank=0,
|
||||||
|
attn_cp_size=2,
|
||||||
|
attn_dp_rank=1,
|
||||||
|
attn_dp_size=2,
|
||||||
|
moe_ep_rank=0,
|
||||||
|
moe_ep_size=1,
|
||||||
|
moe_dp_rank=None,
|
||||||
|
moe_dp_size=1,
|
||||||
|
gpu_id=0,
|
||||||
|
)
|
||||||
|
defaults.update(overrides)
|
||||||
|
return ParallelState(**defaults)
|
||||||
|
|
||||||
|
|
||||||
|
def _fake_group() -> SimpleNamespace:
|
||||||
|
return SimpleNamespace(rank=0, ranks=[0], cpu_group=object())
|
||||||
|
|
||||||
|
|
||||||
|
def _make_receiver(ps: ParallelState) -> SchedulerRequestReceiver:
|
||||||
|
group = _fake_group()
|
||||||
|
return SchedulerRequestReceiver(
|
||||||
|
recv_from_tokenizer=None,
|
||||||
|
recv_from_rpc=None,
|
||||||
|
recv_skipper=None,
|
||||||
|
input_blocker=None,
|
||||||
|
mm_receiver=None,
|
||||||
|
ps=ps,
|
||||||
|
tp_group=group,
|
||||||
|
tp_cpu_group=group,
|
||||||
|
attn_tp_group=group,
|
||||||
|
attn_tp_cpu_group=group,
|
||||||
|
attn_cp_group=group,
|
||||||
|
attn_cp_cpu_group=group,
|
||||||
|
world_group=group,
|
||||||
|
server_args=SimpleNamespace(
|
||||||
|
enable_dp_attention=True,
|
||||||
|
enable_dp_attention_local_control_broadcast=False,
|
||||||
|
),
|
||||||
|
model_config=SimpleNamespace(is_multimodal=False),
|
||||||
|
max_recv_per_poll=-1,
|
||||||
|
stream_output=lambda *args, **kwargs: None,
|
||||||
|
get_last_forward_mode=lambda: None,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class TestPPCPRankOffsets(unittest.TestCase):
|
||||||
|
def test_request_receiver_uses_cp_size_for_pp_recv_rank(self):
|
||||||
|
ps = _make_ps()
|
||||||
|
calls = []
|
||||||
|
|
||||||
|
def fake_point_to_point_pyobj(data, rank, group, src, dst, **kwargs):
|
||||||
|
calls.append((rank, src, dst))
|
||||||
|
return ["req"]
|
||||||
|
|
||||||
|
receiver = _make_receiver(ps)
|
||||||
|
with patch(
|
||||||
|
"sglang.srt.managers.scheduler_components.request_receiver."
|
||||||
|
"point_to_point_pyobj",
|
||||||
|
side_effect=fake_point_to_point_pyobj,
|
||||||
|
):
|
||||||
|
self.assertEqual(receiver._pull_raw_reqs(), ["req"])
|
||||||
|
|
||||||
|
self.assertEqual(calls, [(12, 4, 12)])
|
||||||
|
|
||||||
|
def test_pp_mixin_uses_cp_size_for_pyobj_send_and_recv_rank(self):
|
||||||
|
ps = _make_ps()
|
||||||
|
scheduler = SchedulerPPMixin()
|
||||||
|
scheduler.ps = ps
|
||||||
|
scheduler.world_group = _fake_group()
|
||||||
|
scheduler.attn_tp_group = _fake_group()
|
||||||
|
scheduler.attn_tp_cpu_group = _fake_group()
|
||||||
|
scheduler.attn_cp_group = _fake_group()
|
||||||
|
scheduler.attn_cp_cpu_group = _fake_group()
|
||||||
|
calls = []
|
||||||
|
|
||||||
|
def fake_point_to_point_pyobj(data, rank, group, src, dst, **kwargs):
|
||||||
|
calls.append((rank, src, dst, kwargs.get("async_send", False)))
|
||||||
|
return ["work"]
|
||||||
|
|
||||||
|
with (
|
||||||
|
patch(
|
||||||
|
"sglang.srt.managers.scheduler_pp_mixin.point_to_point_pyobj",
|
||||||
|
side_effect=fake_point_to_point_pyobj,
|
||||||
|
),
|
||||||
|
patch(
|
||||||
|
"sglang.srt.managers.scheduler_pp_mixin.broadcast_pyobj",
|
||||||
|
side_effect=lambda data, *args, **kwargs: data,
|
||||||
|
),
|
||||||
|
):
|
||||||
|
self.assertEqual(
|
||||||
|
scheduler._pp_send_pyobj_to_next_stage(["data"], async_send=True),
|
||||||
|
["work"],
|
||||||
|
)
|
||||||
|
self.assertEqual(scheduler._pp_recv_pyobj_from_prev_stage(), ["work"])
|
||||||
|
|
||||||
|
self.assertEqual(
|
||||||
|
calls,
|
||||||
|
[
|
||||||
|
(12, 12, 4, True),
|
||||||
|
(12, 4, 12, False),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
unittest.main()
|
||||||
Reference in New Issue
Block a user