Skip to content

[sglang-miles] RDT/NIXL weight sync support for Ray scheduler actors - #27723

Merged
maocheng23 merged 11 commits into
sgl-project:sglang-milesfrom
xyuzh:xinyu/rdt-weight-sync
Aug 23, 2026
Merged

[sglang-miles] RDT/NIXL weight sync support for Ray scheduler actors#27723
maocheng23 merged 11 commits into
sgl-project:sglang-milesfrom
xyuzh:xinyu/rdt-weight-sync

Conversation

@xyuzh

@xyuzh xyuzh commented Jun 9, 2026

Copy link
Copy Markdown
Contributor

What

sglang-side support for RDT (Ray Direct Transport / NIXL) weight sync — lets the miles trainer push RL weights to rollout engines via a zero-copy RDMA pull instead of NCCL broadcast. Paired with the miles-side PR (radixark/miles#1313), which has the perf write-up.

Changes

  • ray/scheduler_actor.py — add pull_weights(), which uses ray.experimental.set_target_for_ref to RDMA pre-sharded weight buckets directly into the model's param.data buffers (no intermediate receive buffers / copies).
  • ray/engine.py + ray/data_parallel_controller.py — register SchedulerActors as detached named actors with the http port baked into the name, so the trainer (a different Ray job) can discover them via list_named_actors even when several engines share a node; raise max_concurrency so a concurrent pull_weights is not starved while run_event_loop blocks; set RAY_EXPERIMENTAL_NOSET_CUDA_VISIBLE_DEVICES=1 so the absolute GPU id from get_accelerator_ids() stays valid.
  • server_args.py — add enable_engine_info_bootstrap to start the engine-info bootstrap server and register per-rank parallelism config without the mooncake/verbs P2P transfer-engine seeding.
  • entrypoints/engine.py + model_executor/model_runner.py — honor enable_engine_info_bootstrap; make the P2P transfer-engine memory registration non-fatal so EFA/verbs clusters where it fails don't crash the scheduler (RDT only needs the parallelism config).
  • model_loader/loader.py — make post_load_weights public so the RDT path can invoke it.
  • pyproject.toml — require ray>=2.55.1 (ray.experimental.set_target_for_ref).

Notes

Targeting the sglang-miles integration branch; rebased onto current sglang-miles (#28001, #29339).


CI States

Latest PR Test (Base): ❌ Run #32602261107
Latest PR Test (Extra): ❌ Run #32602260844
Latest PR Test (AMD ROCm 7.2): ➖ No AMD PR run found for this commit.

@gemini-code-assist

Copy link
Copy Markdown
Contributor

Warning

You have reached your daily quota limit. Please wait up to 24 hours and I will start processing your requests again!

@github-actions github-actions Bot added the dependencies Pull requests that update a dependency file label Jun 9, 2026
@xyuzh
xyuzh force-pushed the xinyu/rdt-weight-sync branch 5 times, most recently from 8fabe26 to 3d7ac3f Compare June 29, 2026 21:25
xyuzh added a commit to xyuzh/sglang that referenced this pull request Jun 29, 2026
@xyuzh
xyuzh marked this pull request as ready for review June 29, 2026 21:44
@gemini-code-assist

Copy link
Copy Markdown
Contributor

Warning

You have reached your daily quota limit. Please wait up to 24 hours and I will start processing your requests again!

xyuzh and others added 8 commits August 19, 2026 11:06
Add the sglang-side support for RDT (Ray Direct Transport / NIXL) weight
sync, used by the miles trainer to push weights to rollout engines via a
zero-copy RDMA pull instead of NCCL broadcast.

- scheduler_actor: add pull_weights(), which uses ray.experimental
  set_target_for_ref to RDMA pre-sharded weight buckets directly into the
  model's param.data buffers (no intermediate receive buffers / copies).
- ray/engine: register SchedulerActors as detached named actors with the http
  port baked into the name, so the trainer (a different Ray job) can discover
  them via list_named_actors even when several engines are co-located on one
  node; raise max_concurrency so a concurrent pull_weights is not starved
  while run_event_loop blocks; set
  RAY_EXPERIMENTAL_NOSET_CUDA_VISIBLE_DEVICES=1 so the absolute GPU id from
  get_accelerator_ids() stays valid.
- server_args: add enable_engine_info_bootstrap, plus the
  needs_engine_info_bootstrap() / registers_parallelism_config() predicates
  that fold it in alongside the existing transfer-engine conditions.
- engine / model_runner: gate the bootstrap server and the parallelism-config
  registration on those predicates, so RDT can get /parallelism_config
  WITHOUT the mooncake/verbs P2P transfer-engine seeding.

Requires ray>=2.55.1 for ray.experimental.set_target_for_ref, which is
already the floor in python/pyproject.toml.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…eight-sync

Detached lifetime and threaded actors were applied to every Ray-mode engine.
Add a dedicated flag that implies --enable-engine-info-bootstrap and gate them
on it. Bind the device in pull_weights, which runs off the event-loop thread.
…ackend

launch_server() unconditionally cleared server_args.placement_group, so a
Ray-backend server launched from a parent job could not join an existing
placement group and would auto-create a second one, double-booking the
rollout GPUs. Honor a caller-supplied placement_group instead, and connect
to the running cluster with the caller's runtime env when the launching
process has no Ray context of its own (the mp.Process child loses it).

Add SchedulerActor.register_weight_for_rdt() so the destination pins its
model parameters with NIXL once, rather than re-pinning and re-handshaking
on every RDT flush. Skipped under --enable-memory-saver, where parameter
storages are not stably resident.

Bump the ray extra to >=2.56.0 for ray.experimental.register_nixl_memory.
…e actors

The SchedulerActor name format was inlined in _create_scheduler_actor, so an
external trainer holding the placement bundles could only find the actors by
scanning list_named_actors for substring matches. Move the format into a
get_scheduler_actor_name() helper and re-export it from sglang.srt.ray, so a
caller that knows (node ip, dp/pp/tp rank, port, bundle index) can rebuild the
name and ray.get_actor() it directly.

The placement-group hex is dropped from the name: it is not knowable from the
caller side, and the http port already disambiguates engines co-located on one
node.
… cluster

The Ray-backend launch path connects with ray.init(address="auto"), which
lands in a fresh anonymous namespace when none is given. SchedulerActors
then register there instead of in the namespace the rest of the deployment
uses, so discovery by name from another job has to scan every namespace.
Honor a caller-supplied ray_namespace, alongside the placement group and
runtime env already threaded through server_args.
Callers hosting the HTTP server in their own Ray actor can now call
launch_engine to get the SchedulerActor handles back, then run serve_http
on a thread. With handles returned directly, the schedulers no longer
need detached lifetimes or name-based discovery, so the actor names key
on the placement group ID instead of the server port.
launch_engine cleared server_args.placement_group before launching, so
SchedulerActors fell back to their own scheduling instead of the group
the caller reserved. Honor the override the caller set.
@xyuzh
xyuzh force-pushed the xinyu/rdt-weight-sync branch from 32d793c to 783651b Compare August 19, 2026 18:16
launch_engine and RayEngine now accept placement_group directly and
carry it to _launch_subprocesses through a contextvar, replacing the
dynamic server_args.placement_group attribute that had to be manually
re-attached after every dataclasses.replace.

Pairs with the separate PR that has RayDataParallelController accept
is_custom_pg, which this passes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dependencies Pull requests that update a dependency file

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants