Skip to content

[XPU] Add VLLM_XPU_DEVICE_OFFSET to place a worker on a non-zero card without masking - #53076

Draft
adobrzyn wants to merge 2 commits into
vllm-project:mainfrom
adobrzyn:xpu-device-index-select
Draft

[XPU] Add VLLM_XPU_DEVICE_OFFSET to place a worker on a non-zero card without masking#53076
adobrzyn wants to merge 2 commits into
vllm-project:mainfrom
adobrzyn:xpu-device-index-select

Conversation

@adobrzyn

Copy link
Copy Markdown
Contributor

Purpose

On XPU a worker always binds xpu:{local_rank}, so placing a single-GPU vLLM
instance on a non-zero card currently requires narrowing device visibility
with ZE_AFFINITY_MASK. That masking makes each process see only one device,
which breaks oneCCL/XCCL collectives that span separate processes: oneCCL
requires every device UUID in a communicator to be visible within each
participating process (comm_dev_uuids must be a sub-vector of
node_dev_uuids). With a per-process mask each process sees a single UUID while
the collective spans two, the check fails, and the collective hangs.

Concrete case: RL post-training with a trainer on xpu:0 and a standalone
vllm serve on xpu:1 on the same host. The trainer↔server weight-sync XCCL
group hangs at the first broadcast under masking, and the only workaround so far
has been to fall back to gloo (host-staged, ~15× slower for multi-GB weight
transfers).

Change

Add VLLM_XPU_DEVICE_OFFSET (int, default 0). The physical device index
becomes local_rank + VLLM_XPU_DEVICE_OFFSET, applied consistently to
self.device and to the init-time get_device_properties query, while all
cards remain visible
(no ZE_AFFINITY_MASK). VLLM_XPU_DEVICE_OFFSET=1 places
a single-GPU server on xpu:1 with both cards visible, so a cross-process XCCL
collective can build its 2-device topology and run.

Default 0 is a no-op, so existing single- and multi-card behavior is unchanged.

Why this is not a duplicate of #46226

#46226 ("[XPU] Set per-worker ZE_AFFINITY_MASK for workers") touches the same
files but takes the opposite approach: it isolates workers by masking so
each worker sees exactly one card. That is useful for per-worker memory
profiling / KV sizing, but it is precisely what breaks cross-process oneCCL/XCCL
(above). This PR isolates by device index while keeping every card visible,
which is required for cross-process collectives such as RL weight sync. The two
are complementary (offset defaults to 0, no mask), but they solve different
problems and should not be conflated.

Test plan and results

Validated end-to-end on 2× Intel Arc Pro B60 (torch 2.13.0+xpu, oneCCL 2022.0.0,
Level-Zero 1.32.0), GRPO (TRL) with a standalone vllm serve:

  • Server launched with VLLM_XPU_DEVICE_OFFSET=1 → engine came up on xpu:1
    with both cards visible; trainer stayed on xpu:0.
  • XCCL weight sync worked: the trainer completed all steps (previously it
    hung at the first collective under ZE_AFFINITY_MASK); the fatal oneCCL
    ... is not sub-vector of node_dev_uuids ... narrow device affinity mask
    message no longer appears (0 occurrences).
  • Full model state dict synced over XCCL every step; per-step weight-sync time
    ~1.7× faster than the gloo fallback in the same setup.
  • A minimal 2-rank ProcessGroupXCCL broadcast reproducer confirms the root
    cause independently: masked (1 visible card/process) → hang; unmasked with
    index selection → pass.

Static checks:

python -m py_compile vllm/envs.py vllm/v1/worker/xpu_worker.py   # OK
pre-commit run --files vllm/envs.py vllm/v1/worker/xpu_worker.py

Model evaluation: not applicable — this only changes which physical XPU a
worker binds to; it does not alter model outputs, numerics, or accuracy.

Notes

AI assistance (Claude Code) was used to draft this change; the submitter
reviewed every changed line and ran the validation above on real hardware.

Draft: opening for early feedback on the approach and its relationship to #46226.

On XPU a worker binds xpu:{local_rank}, so putting a single-GPU vLLM
instance on a non-zero card today requires narrowing visibility with
ZE_AFFINITY_MASK. That masking makes each process see only one device,
which breaks oneCCL/XCCL collectives spanning separate processes (oneCCL
requires every communicator device UUID to be visible per process), so
e.g. RL weight sync between a trainer on xpu:0 and a standalone vLLM
server on xpu:1 hangs at the first collective.

Add VLLM_XPU_DEVICE_OFFSET (default 0): the physical device index is
local_rank + offset, applied consistently to the device and to the
init memory query, while all cards stay visible. Setting the offset to
1 places a single-GPU server on xpu:1 with both cards visible, so the
cross-process XCCL collective builds its topology and runs.

Signed-off-by: Agata Dobrzyniewicz <agata.dobrzyniewicz@intel.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@mergify mergify Bot added the intel-gpu Related to Intel GPU label Aug 20, 2026
Add a "Selecting a specific XPU device" subsection describing how to pin
a worker to a non-zero card without ZE_AFFINITY_MASK, and why that is
preferred when a cross-process XCCL collective is involved.

Signed-off-by: Agata Dobrzyniewicz <agata.dobrzyniewicz@intel.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@mergify

mergify Bot commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

Documentation preview: https://vllm--53076.org.readthedocs.build/en/53076/

@mergify mergify Bot added the documentation Improvements or additions to documentation label Aug 20, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation intel-gpu Related to Intel GPU

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant