You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
[XPU] feat: add XCCL checkpoint engine for disaggregated trainer/rollout on Intel GPUs - #26
Adds an xccl checkpoint engine to the XPU accelerator package so a disaggregated trainer/rollout (for example one-step-off or fully-async GRPO) can sync weights on Intel GPUs. It registers when the plugin loads on an XPU host and is selected with:
No verl core change. No Ray collective backend, cupy or vLLM change is needed.
Motivation
verl's nccl checkpoint engine cannot run on XPU: it imports cupy (CUDA only), hard-codes cuda, and broadcasts through ray.util.collective, which has no XCCL backend in upstream Ray. The engine never registers on an XPU image, so there is no usable weight-sync mode for a disaggregated trainer/rollout.
Changes
verl_hardware_plugin/accelerators/xpu/engines/xccl_checkpoint_engine.py: the engine. Same bucketed all_gather + broadcast design, ZMQ metadata channel and rebuild_group option as verl's NCCL engine. The data path is a torch ProcessGroupXCCL: actor rank 0 hosts a TCPStore (OS-assigned port) and publishes its port and a per-round id in MasterMetadata; each rank builds its own process group over a PrefixStore, leaving the default torch.distributed world alone. Staging buffers are plain torch XPU tensors. Only _start_store, _create_process_group, _destroy_process_group and _broadcast touch the transport.
verl_hardware_plugin/accelerators/xpu/registration.py: try-import so xccl registers on load (same pattern as the MLU checkpoint engines).
tests/accelerators/xpu/test_xccl_checkpoint_engine.py: registration, defaults and rank layout (pure Python; skipped without a torch XCCL build).
docs/accelerators/xpu/xccl_checkpoint_engine.md: usage, the settings that mattered, and how to swap the transport later.
multi_sender (relay ranks on the source node) is kept for parity with the NCCL engine but defaults to False: with a real FSDP weight generator and more than one bucket the relay path stalled after the first bucket.
Testing
Environment: 4x Intel Arc Pro B60, one node, image verl-intel-gpu:kahlunte-xpu-main-pinning-20261002 with this branch and upstream-main verl (7e14b6be, no XCCL engine in it) overlaid on PYTHONPATH, VERL_PLATFORM=intel.
pytest plugin_tests/test_xccl_checkpoint_engine.py (this PR's test file, run inside that image): 5 passed.
xccl appeared in CheckpointEngineRegistry with no import error.
All 3 training steps completed (333 s, no errors).
Four weight syncs (initial plus one per step) took 1.85, 0.86, 0.73 and 0.73 s; the vLLM servers loaded the full model after each.
The plugin's ray_device_pin_patch pinned the trainers to xpu:0/xpu:1 and the checkpoint-engine workers to xpu:2/xpu:3.
Pre-commit hooks run on the changed files: ruff, ruff-format, mypy, license header, copyright header, naming and compileall all pass.
Settings that mattered (details in the docs page):
At least 32 CPUs on the job. With 16 the Ray rollout placement group never schedules (the task runner holds 10 CPUs and the trainer group reserves 6), so the run looks hung.
update_weights_bucket_megabytes at least as large as the largest single weight (about 544 MB in fp32 for embed_tokens); 512 MB is refused by verl's own check.
multi_sender=False (the default).
One device per Ray worker. The pinning comes from the plugin's ray_device_pin_patch; with the older plugin bundled in the image over upstream-main core, a vLLM replica shared the trainer's card and the trainer died with UR_RESULT_ERROR_OUT_OF_RESOURCES.
Not verified: multi-node (cross-node XCCL has known problems on oneCCL 2022.x), more than 4 GPUs, models above 0.5B, repeated runs. A 2048 MB-bucket run with vLLM gpu_memory_utilization=0.5 on a node with a known faulty card aborted on a later update with oneCCL ZE_RESULT_ERROR_UNKNOWN; that node is not a clean data point and the combination was not rerun. The engine uses private torch APIs (ProcessGroupXCCL, ProcessGroup._set_default_backend, _register_backend) and mirrors the structure of verl's NCCL engine, so it has to follow upstream changes by hand.
pytest tests/ -v passes (only the new test file was run; the full suite was not)
Manually verified on target hardware (4x B60, see above)
Acceptance Baseline (for new hardware adaptation PRs)
This PR does not add a platform; it adds an optional checkpoint engine to an existing one. scripts/baseline_grpo_gsm8k.sh runs the colocated trainer (verl.trainer.main_ppo) on 8 devices and never goes through a disaggregated checkpoint engine, so it cannot exercise this change. It was not run here.
Ran scripts/baseline_grpo_gsm8k.sh on target hardware (8 devices): not run, see above
Training completed all epochs without error: n/a for this change
critic/rewards/mean shows clear upward trend in first 100 steps: n/a
Curve is consistent with the NVIDIA reference: n/a
Checklist
Code follows the project's style and passes the pre-commit hooks on the changed files
…out on Intel GPUs
Register an 'xccl' checkpoint engine from the XPU accelerator package. Weights move over a torch ProcessGroupXCCL built on a TCPStore hosted by actor rank 0, so no Ray collective backend, cupy or vLLM change is needed. multi_sender defaults to False (the relay path stalls with a real FSDP weight generator). Verified with one-step-off GRPO and live vLLM servers on 4x B60.
Adds an optional xccl checkpoint engine for weight synchronization between disaggregated trainers and rollout workers on Intel GPUs, without changes to verl core.
Changes:
Implements bucketed weight transfer using a dedicated XCCL process group and ZMQ metadata channel.
Registers the engine through the XPU plugin, with single-sender mode enabled by default.
Adds registration and topology tests, plus usage guidance and validation limits.
…et offsets
Rank 0 now publishes bucket metadata only after every consumer's ZeroMQ subscription has been seen (XPUB), and consumers keep their SUB socket across syncs instead of opening a new one each time. Chunk starts inside a bucket are rounded up to 16 bytes by one shared helper used by the sender and the relay, so mixed-dtype buckets can be rebuilt with view(dtype). Adds 7 tests, including a mixed-dtype round trip through verl's merge_weight_chunks.
The reason will be displayed to describe this comment to others. Learn more.
🟡 Changes recommended
With multi_sender=False, excluded trainer ranks still allocate unused XPU buffers—2 GiB for a 1024 MiB bucket—reducing FSDP memory headroom and risking avoidable out-of-memory failures.
Trainer ranks left out of the group no longer allocate 2 x bucket (4 GB at the default 2048 MB). A relay rank allocates one buffer. The multi_sender docstring now states the observation without an unproven cause. Adds 3 tests (16 total). Verified end to end on 4x B60 with the stock config: 3/3 steps.
The reason will be displayed to describe this comment to others. Learn more.
🔵 Needs a closer look
The nccl alias can override a non-Intel platform’s native checkpoint engine and break weight synchronization, while multi-bucket send/receive behavior lacks regression coverage.
load_backends() imports every accelerator's registration module, so a torch build exporting ProcessGroupXCCL reaches this nccl alias even when Intel is not the selected platform. Registering it can replace the selected platform's native checkpoint engine with one that allocates XPU tensors, breaking disaggregated weight synchronization. Keep xccl independently registered, but register the nccl compatibility alias only when Intel is the selected platform.
The automated round-trip test manually constructs a single bucket and only invokes verl's merge helper, so neither this packing loop nor the receiving loop is exercised by the new tests. Padding-triggered flushes, buffer reuse across multiple buckets, and tensors split across buckets therefore have no regression coverage. Please add a round-trip test that drives send_weights() and receive_weights() with XPU allocations and transport mocked, spans at least three buckets, and verifies all reconstructed tensor values.
The one end-to-end stall seen earlier with True is not reproducible after the subscription-wait and buffer fixes: on 4x B60 (Qwen2.5-0.5B, one-step-off GRPO, 3 steps) both this engine with multi_sender=True and verl's own NCCL engine over the Ray XCCL backend completed all steps with the relay rank active. Only one run per arm on a 0.5B model; larger models and multi-node are untested.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds an
xcclcheckpoint engine to the XPU accelerator package so a disaggregated trainer/rollout (for example one-step-off or fully-async GRPO) can sync weights on Intel GPUs. It registers when the plugin loads on an XPU host and is selected with:No verl core change. No Ray collective backend, cupy or vLLM change is needed.
Motivation
verl's
ncclcheckpoint engine cannot run on XPU: it imports cupy (CUDA only), hard-codescuda, and broadcasts throughray.util.collective, which has no XCCL backend in upstream Ray. The engine never registers on an XPU image, so there is no usable weight-sync mode for a disaggregated trainer/rollout.Changes
verl_hardware_plugin/accelerators/xpu/engines/xccl_checkpoint_engine.py: the engine. Same bucketed all_gather + broadcast design, ZMQ metadata channel andrebuild_groupoption as verl's NCCL engine. The data path is a torchProcessGroupXCCL: actor rank 0 hosts aTCPStore(OS-assigned port) and publishes its port and a per-round id inMasterMetadata; each rank builds its own process group over aPrefixStore, leaving the default torch.distributed world alone. Staging buffers are plain torch XPU tensors. Only_start_store,_create_process_group,_destroy_process_groupand_broadcasttouch the transport.verl_hardware_plugin/accelerators/xpu/registration.py:try-import soxcclregisters on load (same pattern as the MLU checkpoint engines).tests/accelerators/xpu/test_xccl_checkpoint_engine.py: registration, defaults and rank layout (pure Python; skipped without a torch XCCL build).docs/accelerators/xpu/xccl_checkpoint_engine.md: usage, the settings that mattered, and how to swap the transport later.multi_sender(relay ranks on the source node) is kept for parity with the NCCL engine but defaults toFalse: with a real FSDP weight generator and more than one bucket the relay path stalled after the first bucket.Testing
Environment: 4x Intel Arc Pro B60, one node, image
verl-intel-gpu:kahlunte-xpu-main-pinning-20261002with this branch and upstream-main verl (7e14b6be, no XCCL engine in it) overlaid onPYTHONPATH,VERL_PLATFORM=intel.pytest plugin_tests/test_xccl_checkpoint_engine.py(this PR's test file, run inside that image): 5 passed.verl.experimental.one_step_off_policy.main_ppo), 2 trainer GPUs + 2 vLLM rollout GPUs, Qwen2.5-0.5B-Instruct on gsm8k, batch 32, 3 steps, 32 CPUs, 52 GiB host memory, bucket 1024 MB, vLLMgpu_memory_utilization=0.3,RAY_EXPERIMENTAL_NOSET_ONEAPI_DEVICE_SELECTORunset.xcclappeared inCheckpointEngineRegistrywith no import error.ray_device_pin_patchpinned the trainers toxpu:0/xpu:1and the checkpoint-engine workers toxpu:2/xpu:3.Settings that mattered (details in the docs page):
update_weights_bucket_megabytesat least as large as the largest single weight (about 544 MB in fp32 forembed_tokens); 512 MB is refused by verl's own check.multi_sender=False(the default).ray_device_pin_patch; with the older plugin bundled in the image over upstream-main core, a vLLM replica shared the trainer's card and the trainer died withUR_RESULT_ERROR_OUT_OF_RESOURCES.Not verified: multi-node (cross-node XCCL has known problems on oneCCL 2022.x), more than 4 GPUs, models above 0.5B, repeated runs. A 2048 MB-bucket run with vLLM
gpu_memory_utilization=0.5on a node with a known faulty card aborted on a later update with oneCCLZE_RESULT_ERROR_UNKNOWN; that node is not a clean data point and the combination was not rerun. The engine uses private torch APIs (ProcessGroupXCCL,ProcessGroup._set_default_backend,_register_backend) and mirrors the structure of verl's NCCL engine, so it has to follow upstream changes by hand.pytest tests/ -vpasses (only the new test file was run; the full suite was not)Acceptance Baseline (for new hardware adaptation PRs)
This PR does not add a platform; it adds an optional checkpoint engine to an existing one.
scripts/baseline_grpo_gsm8k.shruns the colocated trainer (verl.trainer.main_ppo) on 8 devices and never goes through a disaggregated checkpoint engine, so it cannot exercise this change. It was not run here.scripts/baseline_grpo_gsm8k.shon target hardware (8 devices): not run, see abovecritic/rewards/meanshows clear upward trend in first 100 steps: n/aChecklist
pre-commithooks on the changed filesdocs/accelerators/xpu/xccl_checkpoint_engine.md)