Pipeline parallelism on the SGLang rollout engine is currently blocked in three places:
| file |
guard |
unirl/rollout/engine/sglang/config.py:146 |
pp_size>1 is not supported yet — UniRL Handle would spawn one engine per pp_rank while SGLang spawns its own PP scheduler subprocesses, double-booking GPUs |
unirl/distributed/weight_sync/full/nccl.py:73 |
NCCLWeightSync.connect: rollout pp_size>1 is not implemented |
unirl/distributed/group/dispatch.py:178 |
rejected because auto-backward cannot propagate gradients across pipeline stages |
The config comment names two directions as future work — per-stage rank_offset routing, and single-engine PP fan-out.
Reading sglang/engine.py, TP already resolves the same ownership question by delegating: every TP rank gets an engine instance, but only tp_rank=0 is live (engine.py:76-88 turns the rest into no-op shells), and that one instance receives the whole TP group via cuda_visible_devices=tp_visible_devices plus runtime_overrides["tp_size"] (engine.py:121-124, :153), so SGLang does the sharding internally.
Questions:
- Is single-engine PP fan-out (same delegation pattern as TP, gated on
pp_rank == 0 and tp_rank == 0) the intended direction, or is per-stage rank_offset routing preferred?
NCCLWeightSync.connect currently derives the broadcast group from num_rollout_gpus and tp_size. Under the delegation model the rollout side would still expose one live engine per PP group — is reworking that rank accounting the main blocker, or is there more?
- The
dispatch.py guard is about gradients crossing pipeline stages. Rollout engines don't backprop, so is that guard incidental to this path, or does it apply for a reason I'm missing?
- Is anyone working on this already?
Happy to take a piece of it if the design direction is settled. Asking before writing anything, since the config comment suggests the approach is still open.
Pipeline parallelism on the SGLang rollout engine is currently blocked in three places:
unirl/rollout/engine/sglang/config.py:146pp_size>1 is not supported yet— UniRL Handle would spawn one engine per pp_rank while SGLang spawns its own PP scheduler subprocesses, double-booking GPUsunirl/distributed/weight_sync/full/nccl.py:73NCCLWeightSync.connect: rollout pp_size>1 is not implementedunirl/distributed/group/dispatch.py:178The config comment names two directions as future work — per-stage
rank_offsetrouting, and single-engine PP fan-out.Reading
sglang/engine.py, TP already resolves the same ownership question by delegating: every TP rank gets an engine instance, but onlytp_rank=0is live (engine.py:76-88turns the rest into no-op shells), and that one instance receives the whole TP group viacuda_visible_devices=tp_visible_devicesplusruntime_overrides["tp_size"](engine.py:121-124,:153), so SGLang does the sharding internally.Questions:
pp_rank == 0 and tp_rank == 0) the intended direction, or is per-stagerank_offsetrouting preferred?NCCLWeightSync.connectcurrently derives the broadcast group fromnum_rollout_gpusandtp_size. Under the delegation model the rollout side would still expose one live engine per PP group — is reworking that rank accounting the main blocker, or is there more?dispatch.pyguard is about gradients crossing pipeline stages. Rollout engines don't backprop, so is that guard incidental to this path, or does it apply for a reason I'm missing?Happy to take a piece of it if the design direction is settled. Asking before writing anything, since the config comment suggests the approach is still open.