Skip to content
Closed
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
3 changes: 2 additions & 1 deletion examples/ar/qwen3_5_moe_grpo_35b_a3b_geo3k_mc_sglang.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,8 @@ rollout:
enable_thinking: false
engine_kwargs:
# Colocate MoE + geo3k: cuda graph OFF and mem_fraction_static 0.15
# (4B dense preset is 0.3 + cuda graph ON @ bs 16).
# (4B dense baseline is 0.3 + cuda graph ON @ bs 16). Unlike that
# baseline, attention_backend is not pinned; SGLang selects it by platform.
# See unirl/rollout/README.md (SGLang AR knobs).
mem_fraction_static: 0.15
disable_cuda_graph: true
Expand Down
4 changes: 2 additions & 2 deletions examples/ar/qwen3_dppo_4b_base_dapo_sglang.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -133,8 +133,8 @@ rollout:
# FULL fine-tuning: lower the SRT KV reservation to give FSDP headroom for
# the per-rollout full-weight all-gather. No LoRA pool — full dense weights
# pushed via TensorWeightSync. CUDA graph ON for decode speed, but capped at
# cuda_graph_max_bs=16 so its captured buffers stay small (the default 256
# would balloon server memory + compete with the weight push).
# cuda_graph_max_bs=16 so its captured buffers stay small (the auto-tuned
# default can be much larger and compete with the weight push).
mem_fraction_static: 0.3
skip_server_warmup: true
attention_backend: triton
Expand Down
4 changes: 2 additions & 2 deletions examples/ar/qwen3_drgrpo_4b_base_dapo_sglang.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -125,8 +125,8 @@ rollout:
# FULL fine-tuning: lower the SRT KV reservation to give FSDP headroom for
# the per-rollout full-weight all-gather. No LoRA pool — full dense weights
# pushed via TensorWeightSync. CUDA graph ON for decode speed, but capped at
# cuda_graph_max_bs=16 so its captured buffers stay small (the default 256
# would balloon server memory + compete with the weight push).
# cuda_graph_max_bs=16 so its captured buffers stay small (the auto-tuned
# default can be much larger and compete with the weight push).
mem_fraction_static: 0.3
skip_server_warmup: true
attention_backend: triton
Expand Down
4 changes: 2 additions & 2 deletions examples/ar/qwen3_drpo_4b_base_dapo_sglang.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -143,8 +143,8 @@ rollout:
# FULL fine-tuning: lower the SRT KV reservation to give FSDP headroom for
# the per-rollout full-weight all-gather. No LoRA pool — full dense weights
# pushed via TensorWeightSync. CUDA graph ON for decode speed, but capped at
# cuda_graph_max_bs=16 so its captured buffers stay small (the default 256
# would balloon server memory + compete with the weight push).
# cuda_graph_max_bs=16 so its captured buffers stay small (the auto-tuned
# default can be much larger and compete with the weight push).
mem_fraction_static: 0.3
skip_server_warmup: true
attention_backend: triton
Expand Down
4 changes: 2 additions & 2 deletions examples/ar/qwen3_drpo_4b_veomni_sp_sglang.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -135,8 +135,8 @@ rollout:
# FULL fine-tuning: lower the SRT KV reservation to give FSDP headroom for
# the per-rollout full-weight all-gather. No LoRA pool — full dense weights
# pushed via TensorWeightSync. CUDA graph ON for decode speed, but capped at
# cuda_graph_max_bs=16 so its captured buffers stay small (the default 256
# would balloon server memory + compete with the weight push).
# cuda_graph_max_bs=16 so its captured buffers stay small (the auto-tuned
# default can be much larger and compete with the weight push).
mem_fraction_static: 0.3
skip_server_warmup: true
attention_backend: triton
Expand Down
4 changes: 2 additions & 2 deletions examples/ar/qwen3_grpo_4b_base_dapo_sglang.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -132,8 +132,8 @@ rollout:
# FULL fine-tuning: lower the SRT KV reservation to give FSDP headroom for
# the per-rollout full-weight all-gather. No LoRA pool — full dense weights
# pushed via TensorWeightSync. CUDA graph ON for decode speed, but capped at
# cuda_graph_max_bs=16 so its captured buffers stay small (the default 256
# would balloon server memory + compete with the weight push).
# cuda_graph_max_bs=16 so its captured buffers stay small (the auto-tuned
# default can be much larger and compete with the weight push).
mem_fraction_static: 0.3
skip_server_warmup: true
attention_backend: triton
Expand Down
4 changes: 2 additions & 2 deletions examples/ar/qwen3_grpo_4b_veomni_sp_sglang.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -136,8 +136,8 @@ rollout:
# FULL fine-tuning: lower the SRT KV reservation to give FSDP headroom for
# the per-rollout full-weight all-gather. No LoRA pool — full dense weights
# pushed via TensorWeightSync. CUDA graph ON for decode speed, but capped at
# cuda_graph_max_bs=16 so its captured buffers stay small (the default 256
# would balloon server memory + compete with the weight push).
# cuda_graph_max_bs=16 so its captured buffers stay small (the auto-tuned
# default can be much larger and compete with the weight push).
mem_fraction_static: 0.3
skip_server_warmup: true
attention_backend: triton
Expand Down
4 changes: 2 additions & 2 deletions examples/ar/qwen3_grpo_vanilla_4b_base_dapo_sglang.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -134,8 +134,8 @@ rollout:
# FULL fine-tuning: lower the SRT KV reservation to give FSDP headroom for
# the per-rollout full-weight all-gather. No LoRA pool — full dense weights
# pushed via TensorWeightSync. CUDA graph ON for decode speed, but capped at
# cuda_graph_max_bs=16 so its captured buffers stay small (the default 256
# would balloon server memory + compete with the weight push).
# cuda_graph_max_bs=16 so its captured buffers stay small (the auto-tuned
# default can be much larger and compete with the weight push).
mem_fraction_static: 0.3
skip_server_warmup: true
attention_backend: triton
Expand Down
2 changes: 1 addition & 1 deletion examples/ar/qwen3_ppo_4b_base_dapo_sglang.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ rollout:
# Colocate full-FT preset (unirl/rollout/README.md): lower the SRT KV
# reservation so FSDP can all-gather full dense weights. enable_lora must
# stay false for TensorWeightSync. CUDA graph ON, cuda_graph_max_bs=16
# (SGLang default 256 would balloon server memory + compete with the push).
# (the auto-tuned default can be much larger and compete with the push).
mem_fraction_static: 0.3
skip_server_warmup: true
attention_backend: triton
Expand Down
42 changes: 27 additions & 15 deletions unirl/rollout/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -113,35 +113,47 @@ change surface:

## SGLang AR knobs

Qwen3 AR recipes (`examples/ar/qwen3_*_sglang*.yaml`) share one **colocate
full-FT** `engine_kwargs` preset. Async/separate and larger-model recipes keep
the same keys and change the memory numbers. Typed `SGLangEngineConfig` fields
overlay `engine_kwargs`; reserved ports always win. Keys that are not live
SGLang `ServerArgs` fields warn at boot (or raise if
`UNIRL_SGLANG_STRICT_SERVER_ARGS=1`) — see [`engine/README.md`](engine/README.md).
The 4B dense Qwen3 AR recipes (`examples/ar/qwen3_*_sglang*.yaml`) share a
**colocate full-FT** `engine_kwargs` baseline. Async/separate, larger-model,
MoE, and LoRA recipes deliberately deviate as described below and in their
inline comments. Typed `SGLangEngineConfig` fields overlay `engine_kwargs`;
reserved ports always win. Keys that are not live SGLang `ServerArgs` fields
warn at boot (or raise if `UNIRL_SGLANG_STRICT_SERVER_ARGS=1`) — see
[`engine/README.md`](engine/README.md).

**Colocate full-FT preset** (FSDP train shard time-shares the GPU; TensorWeightSync):
**4B dense colocate full-FT baseline** (FSDP train shard time-shares the GPU;
TensorWeightSync):

| Knob | Preset | Why |
|---|---|---|
| `mem_fraction_static` | `0.3` | Lower SRT KV reservation so FSDP can all-gather full dense weights. `server_intent()` defaults to `0.88` if omitted — too high for colocate. |
| `enable_lora` | `false` | TensorWeightSync pushes full dense weights; a LoRA pool would be the wrong receive path. |
| `cuda_graph_max_bs` | `16` | CUDA graph stays on (`disable_cuda_graph: false`); SGLang's default `256` captures buffers that fight the weight push. |
| `skip_server_warmup` | `true` | Skip SRT warmup on every colocated boot/wake. |
| `cuda_graph_max_bs` | `16` | CUDA graph stays on (`disable_cuda_graph: false`); SGLang otherwise auto-tunes the capture cap from GPU memory and TP size, often reserving much larger buffers that fight the weight push. |
| `skip_server_warmup` | `true` | Skip SRT startup warmup on the 4B colocate path; this knob does not control `wake_up()`. |
| `attention_backend` | `triton` | Matches the in-tree 4B full-FT recipes. |

**Larger / MoE colocate:** memory and graph settings are recipe-specific
(`mem_fraction_static` ranges from `0.15` to `0.5`; graph caps include `64` and
`128`, and one VLM MoE recipe disables graphs). Several MoE/CPPO recipes set the
typed `skip_server_warmup: false`, which wins over `engine_kwargs`, so startup
warmup runs. The VLM MoE geo3k recipe also leaves `attention_backend` to
SGLang's platform selection.

**Async / separate** (`*_sglang_async.yaml`): the engine owns the GPU (no colocated
FSDP shard). Raise `mem_fraction_static` to `0.8` and leave the other preset keys
as-is; keep headroom for NCCL weight-receive buffers. Sync is `NCCLWeightSync`.
FSDP shard). Raise `mem_fraction_static` to `0.8` and leave the other 4B baseline
keys as-is; keep headroom for NCCL weight-receive buffers. Sync is
`NCCLWeightSync`.

**LoRA colocate** (`*_sglang_lora.yaml`): `enable_lora: true` plus the SGLang LoRA
pool knobs. This is the exception to the full-FT `enable_lora: false` receive path
(`LocalLoraWeightSync` instead of TensorWeightSync). Memory numbers are recipe-specific.

**Reserved ports:** `SGLangPorts.reserve()` binds HTTP `port` and `nccl_port` on
the engine's node. Server port is capped at 35535 because SGLang derives
`grpc_port = port + 30000`. Do not set `port` / `nccl_port` in `engine_kwargs` —
the reserved sockets overwrite them.
**Reserved ports:** `SGLangPorts.reserve()` selects candidate HTTP `port` and
`nccl_port` values by binding temporary sockets on the engine's node, then
closes those sockets so SGLang can bind (the usual bind-to-zero TOCTOU gap
remains). With pinned SGLang 0.5.12.post1, the server port is capped at 55535
because an enabled gRPC endpoint defaults to `grpc_port = port + 10000`. Do not
set `port` / `nccl_port` in `engine_kwargs` — the selected values overwrite them.

## Gotchas

Expand Down
14 changes: 3 additions & 11 deletions unirl/rollout/engine/sglang/backends/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,15 +28,6 @@
)


def _strict_dropped_server_args() -> bool:
return os.environ.get(_STRICT_SERVER_ARGS_ENV, "").strip().lower() in {"1", "true", "yes", "on"}


def _unknown_server_arg_keys(server_intent: Dict[str, Any], allowed: set[str]) -> List[str]:
"""Intent keys that are neither live ``ServerArgs`` fields nor UniRL-only."""
return sorted(key for key in server_intent if key not in allowed and key not in _UNIRL_ONLY_INTENT_KEYS)


def _filter_server_args_or_raise(
server_intent: Dict[str, Any],
*,
Expand All @@ -56,14 +47,15 @@ def _filter_server_args_or_raise(
"Upgrade SGLang to a build that supports these fields, or remove the explicit UniRL "
"rollout config that depends on them."
)
dropped = _unknown_server_arg_keys(server_intent, allowed)
dropped = sorted(key for key in server_intent if key not in allowed and key not in _UNIRL_ONLY_INTENT_KEYS)
if dropped:
message = (
f"SGLang {backend_name} backend dropping unknown ServerArgs keys: {dropped}. "
"They are not fields on the installed SGLang ServerArgs (typo or version skew). "
f"Set {_STRICT_SERVER_ARGS_ENV}=1 to fail closed."
)
if _strict_dropped_server_args():
strict = os.environ.get(_STRICT_SERVER_ARGS_ENV, "").strip().lower() in {"1", "true", "yes", "on"}
if strict:
raise RuntimeError(message)
logger.warning(message)
return {k: v for k, v in server_intent.items() if k != _REQUIRED_SERVER_ARGS_METADATA_KEY and k in allowed}
Expand Down
2 changes: 1 addition & 1 deletion unirl/rollout/engine/sglang/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
from unirl.rollout.engine.base import BaseEngineConfig
from unirl.rollout.engine.ports import ReservedPorts

_SGLANG_GRPC_PORT_OFFSET = 30000
_SGLANG_GRPC_PORT_OFFSET = 10000
_SGLANG_MAX_DERIVED_GRPC_BASE_PORT = 65535 - _SGLANG_GRPC_PORT_OFFSET
_SGLANG_SAFE_SERVER_PORT_MIN = 1024
_REQUIRED_SERVER_ARGS_METADATA_KEY = "_unirl_required_server_args"
Expand Down
Loading