Skip to content

vllm.distributed.weight_transfer.nccl_engine

NCCL-based (dense) weight transfer engine.

Classes:

NCCLTrainerInitInfo dataclass

Bases: TrainerInitInfo

Trainer-side init info for the dense NCCL weight transfer backend.

The sender opens its endpoint as NCCL rank 0, so it needs no rank_offset. world_size is the full trainer+worker NCCL group size. rank (from TrainerInitInfo) identifies this trainer process; rank 0 is the sender.

packed / buffer sizes are the transfer's wire params. The trainer propagates them to the worker at trainer_init so the two sides cannot disagree. Note this defaults to packed, unlike the worker-side NCCLWeightTransferInitInfo, whose default only applies when no trainer ships a value. backend is the factory dispatch key.

Source code in vllm/distributed/weight_transfer/nccl_engine.py
@dataclass
class NCCLTrainerInitInfo(TrainerInitInfo):
    """Trainer-side init info for the dense NCCL weight transfer backend.

    The sender opens its endpoint as NCCL rank 0, so it needs no `rank_offset`.
    `world_size` is the full trainer+worker NCCL group size. `rank` (from
    `TrainerInitInfo`) identifies this trainer process; rank 0 is the sender.

    `packed` / buffer sizes are the transfer's wire params. The trainer
    propagates them to the worker at `trainer_init` so the two sides cannot
    disagree. Note this defaults to packed, unlike the worker-side
    `NCCLWeightTransferInitInfo`, whose default only applies when no trainer
    ships a value. `backend` is the factory dispatch key."""

    backend: ClassVar[str] = "nccl"

    master_address: str
    master_port: int
    world_size: int
    packed: bool = True
    packed_buffer_size_bytes: int = DEFAULT_PACKED_BUFFER_SIZE_BYTES
    packed_num_buffers: int = DEFAULT_PACKED_NUM_BUFFERS

NCCLTrainerWeightTransferEngine

Bases: TrainerWeightTransferEngine[NCCLTrainerInitInfo]

Trainer-side NCCL weight transfer engine.

On the sender (rank 0) holds the NCCL communicator and drives the full update round trip: it runs the inference-side update_weights concurrently with the trainer-side broadcast (both rendezvous inside the same NCCL calls), then finishes the update. Non-sender trainer ranks hold no communicator; they only iterate the source to stay in the trainer-side collective (e.g. FSDP full_tensor()) and skip the client RPCs and the broadcast (all guarded on is_sender).

packed / buffer sizes come from NCCLTrainerInitInfo; the sender propagates them to the worker at trainer_init (on the worker-side init info), so per-round payloads carry only parameter metadata.

Source code in vllm/distributed/weight_transfer/nccl_engine.py
class NCCLTrainerWeightTransferEngine(TrainerWeightTransferEngine[NCCLTrainerInitInfo]):
    """Trainer-side NCCL weight transfer engine.

    On the sender (rank 0) holds the NCCL communicator and drives the full
    update round trip: it runs the inference-side `update_weights` concurrently
    with the trainer-side broadcast (both rendezvous inside the same NCCL
    calls), then finishes the update. Non-sender trainer ranks hold no
    communicator; they only iterate the source to stay in the trainer-side
    collective (e.g. FSDP `full_tensor()`) and skip the client RPCs and the
    broadcast (all guarded on `is_sender`).

    `packed` / buffer sizes come from `NCCLTrainerInitInfo`; the sender
    propagates them to the worker at `trainer_init` (on the worker-side init
    info), so per-round payloads carry only parameter metadata.
    """

    init_info_cls = NCCLTrainerInitInfo

    def __init__(
        self,
        *,
        client: VLLMWeightSyncClient,
        source: WeightSource,
        is_sender: bool = True,
        packed: bool = True,
        packed_buffer_size_bytes: int = DEFAULT_PACKED_BUFFER_SIZE_BYTES,
        packed_num_buffers: int = DEFAULT_PACKED_NUM_BUFFERS,
    ) -> None:
        super().__init__(client=client, source=source, is_sender=is_sender)
        self.packed = packed
        self.packed_buffer_size_bytes = packed_buffer_size_bytes
        self.packed_num_buffers = packed_num_buffers
        self.model_update_group: PyNcclCommunicator | None = None

    @classmethod
    def trainer_init(
        cls,
        init_info: NCCLTrainerInitInfo,
        *,
        client: VLLMWeightSyncClient,
        source: WeightSource | None = None,
    ) -> Self:
        if source is None:
            raise ValueError("NCCL trainer weight transfer requires a WeightSource.")
        engine = cls(
            client=client,
            source=source,
            is_sender=init_info.is_sender,
            packed=init_info.packed,
            packed_buffer_size_bytes=init_info.packed_buffer_size_bytes,
            packed_num_buffers=init_info.packed_num_buffers,
        )
        if not engine.is_sender:
            # Non-sender trainer ranks aren't part of the transfer NCCL group and
            # don't drive the inference side; they only participate in the
            # trainer-side gather during send_weights.
            return engine

        # Workers sit at rank_offset 1, after the single trainer sender rank 0.
        worker_init_info = NCCLWeightTransferInitInfo(
            master_address=init_info.master_address,
            master_port=init_info.master_port,
            rank_offset=1,
            world_size=init_info.world_size,
            packed=init_info.packed,
            packed_buffer_size_bytes=init_info.packed_buffer_size_bytes,
            packed_num_buffers=init_info.packed_num_buffers,
        )

        # The inference workers block inside init_weight_transfer_engine waiting
        # for the NCCL rendezvous, so we kick that off on a side thread while we
        # open the trainer endpoint (rank 0); both sides must rendezvous together.
        with ThreadPoolExecutor(max_workers=1) as exe:
            future = exe.submit(
                engine.client.init_weight_transfer_engine, asdict(worker_init_info)
            )
            engine.model_update_group = open_trainer_endpoint(init_info)
            future.result()  # surface any inference-side init error

        return engine

    def send_weights(self) -> None:
        assert self.source is not None  # guaranteed by trainer_init / __init__
        source = self.source

        # Metadata is declared without gathering. For Megatron it is itself a
        # collective, so every rank runs it; only the sender ships it.
        meta = source.metadata()

        if not self.is_sender:
            # Non-sender ranks only join the trainer-side gather collective.
            self._broadcast(source, meta)
            self._post_send_sync()
            return

        update_info = NCCLWeightTransferUpdateInfo(
            names=[m.name for m in meta],
            dtype_names=[str(m.dtype).split(".")[-1] for m in meta],
            shapes=[list(m.shape) for m in meta],
        )

        self.client.start_weight_update()
        # update_weights (workers receive) must run concurrently with the
        # trainer-side broadcast — both rendezvous inside the same NCCL calls.
        exe = ThreadPoolExecutor(max_workers=1)
        try:
            future = exe.submit(self.client.update_weights, asdict(update_info))
            # Cheap best-effort: if update_weights already failed (e.g. a bad
            # request rejected before any NCCL call), surface it now instead of
            # hanging in broadcast waiting for a peer that will never arrive.
            if future.done():
                future.result()
            self._broadcast(source, meta)
            future.result()  # surface inference-side errors
        finally:
            # Never wait for the RPC thread here. If the broadcast raised, the
            # worker is still blocked in the matching NCCL call and will never
            # return, so joining would turn the error into a permanent hang.
            # Let the exception out instead; the transfer group is unusable
            # either way and the caller has to tear it down. (The orphaned
            # thread is still joined at interpreter exit, so a caller that wants
            # to exit cleanly after such a failure must not wait on it.)
            exe.shutdown(wait=False)
        self.client.finish_weight_update()
        self._post_send_sync()

    def _broadcast(self, source: WeightSource, meta: list[ParamMeta]) -> None:
        """Iterate the source (materializing each tensor — a collective on all
        ranks) and, on the sender, broadcast from rank 0, packed or one-by-one.
        Non-sender ranks only replay the iteration to stay in the collective."""
        if not self.is_sender:
            for _ in source:
                pass
            return

        assert self.model_update_group is not None, (
            "trainer_init() must be called before _broadcast()."
        )
        pairs = self._checked_iter(source, meta)
        if self.packed:
            packed_nccl_broadcast_producer(
                iterator=pairs,
                group=self.model_update_group,
                src=0,
                post_iter_func=lambda item: item[1],
                buffer_size_bytes=self.packed_buffer_size_bytes,
                num_buffers=self.packed_num_buffers,
            )
        else:
            stream = torch.cuda.current_stream()
            for _name, tensor in pairs:
                # NCCL sends `numel` elements straight from `data_ptr()`, so a
                # non-contiguous view would ship whatever follows its base
                # pointer. Keep the copy referenced until the broadcast is
                # enqueued. (The packed path linearizes in `pack_tensors`.)
                send = tensor if tensor.is_contiguous() else tensor.contiguous()
                self.model_update_group.broadcast(send, src=0, stream=stream)

    @staticmethod
    def _checked_iter(
        source: WeightSource, meta: list[ParamMeta]
    ) -> Iterator[tuple[str, torch.Tensor]]:
        """Yield the source's pairs, checking each against what the worker was
        told to expect.

        The worker sizes its receive buffers — and in packed mode cuts its chunk
        boundaries — from the update info, which is built from `metadata()`. If
        iteration disagrees with it, the two sides split the stream differently
        and the transfer hangs in NCCL or loads garbage. Checking here costs one
        comparison per parameter and turns that into an error naming the first
        divergent parameter. Sender-only: under pipeline parallelism a
        non-sender's yielded tensor is not meaningful.
        """
        sent = 0
        for name, tensor in source:
            if sent >= len(meta):
                raise ValueError(
                    f"WeightSource yielded more parameters than metadata() "
                    f"declared ({len(meta)}); first extra is {name!r}."
                )
            expected = meta[sent]
            if (
                name != expected.name
                or tensor.dtype != expected.dtype
                or tuple(tensor.shape) != expected.shape
            ):
                raise ValueError(
                    "WeightSource metadata() disagrees with iteration at index "
                    f"{sent}: declared {expected.name!r} "
                    f"{expected.dtype} {tuple(expected.shape)}, got {name!r} "
                    f"{tensor.dtype} {tuple(tensor.shape)}. Both channels must "
                    "enumerate the same parameters in the same order."
                )
            sent += 1
            yield name, tensor
        if sent != len(meta):
            raise ValueError(
                f"WeightSource yielded {sent} parameters but metadata() "
                f"declared {len(meta)}; the worker is waiting for the rest."
            )

    def _post_send_sync(self) -> None:
        """Wait for this rank's transfer work to land before returning.

        Broadcasts are only *enqueued* by `send_weights`: the unpacked path on
        the current stream, the packed path on the producer's own streams (which
        it drains itself). Waiting here lets a caller mutate parameters, or
        start the next step on another stream, as soon as `send_weights`
        returns, instead of silently depending on same-stream ordering. Every
        rank waits: a non-sender's `full_tensor()` gathers feed the sender's
        broadcast, so they must have landed before it may touch its shards.

        Unlike IPC there is no cross-rank barrier here. Nothing in this backend
        outlives the collective it travelled in (IPC's barrier keeps shared
        buffers alive until every consumer has opened them), so a barrier would
        only add a dependency on the default process group that this backend
        otherwise does not have.
        """
        if torch.cuda.is_available():
            torch.cuda.current_stream().synchronize()

    def shutdown(self) -> None:
        self.model_update_group = None

_broadcast(source, meta)

Iterate the source (materializing each tensor — a collective on all ranks) and, on the sender, broadcast from rank 0, packed or one-by-one. Non-sender ranks only replay the iteration to stay in the collective.

Source code in vllm/distributed/weight_transfer/nccl_engine.py
def _broadcast(self, source: WeightSource, meta: list[ParamMeta]) -> None:
    """Iterate the source (materializing each tensor — a collective on all
    ranks) and, on the sender, broadcast from rank 0, packed or one-by-one.
    Non-sender ranks only replay the iteration to stay in the collective."""
    if not self.is_sender:
        for _ in source:
            pass
        return

    assert self.model_update_group is not None, (
        "trainer_init() must be called before _broadcast()."
    )
    pairs = self._checked_iter(source, meta)
    if self.packed:
        packed_nccl_broadcast_producer(
            iterator=pairs,
            group=self.model_update_group,
            src=0,
            post_iter_func=lambda item: item[1],
            buffer_size_bytes=self.packed_buffer_size_bytes,
            num_buffers=self.packed_num_buffers,
        )
    else:
        stream = torch.cuda.current_stream()
        for _name, tensor in pairs:
            # NCCL sends `numel` elements straight from `data_ptr()`, so a
            # non-contiguous view would ship whatever follows its base
            # pointer. Keep the copy referenced until the broadcast is
            # enqueued. (The packed path linearizes in `pack_tensors`.)
            send = tensor if tensor.is_contiguous() else tensor.contiguous()
            self.model_update_group.broadcast(send, src=0, stream=stream)

_checked_iter(source, meta) staticmethod

Yield the source's pairs, checking each against what the worker was told to expect.

The worker sizes its receive buffers — and in packed mode cuts its chunk boundaries — from the update info, which is built from metadata(). If iteration disagrees with it, the two sides split the stream differently and the transfer hangs in NCCL or loads garbage. Checking here costs one comparison per parameter and turns that into an error naming the first divergent parameter. Sender-only: under pipeline parallelism a non-sender's yielded tensor is not meaningful.

Source code in vllm/distributed/weight_transfer/nccl_engine.py
@staticmethod
def _checked_iter(
    source: WeightSource, meta: list[ParamMeta]
) -> Iterator[tuple[str, torch.Tensor]]:
    """Yield the source's pairs, checking each against what the worker was
    told to expect.

    The worker sizes its receive buffers — and in packed mode cuts its chunk
    boundaries — from the update info, which is built from `metadata()`. If
    iteration disagrees with it, the two sides split the stream differently
    and the transfer hangs in NCCL or loads garbage. Checking here costs one
    comparison per parameter and turns that into an error naming the first
    divergent parameter. Sender-only: under pipeline parallelism a
    non-sender's yielded tensor is not meaningful.
    """
    sent = 0
    for name, tensor in source:
        if sent >= len(meta):
            raise ValueError(
                f"WeightSource yielded more parameters than metadata() "
                f"declared ({len(meta)}); first extra is {name!r}."
            )
        expected = meta[sent]
        if (
            name != expected.name
            or tensor.dtype != expected.dtype
            or tuple(tensor.shape) != expected.shape
        ):
            raise ValueError(
                "WeightSource metadata() disagrees with iteration at index "
                f"{sent}: declared {expected.name!r} "
                f"{expected.dtype} {tuple(expected.shape)}, got {name!r} "
                f"{tensor.dtype} {tuple(tensor.shape)}. Both channels must "
                "enumerate the same parameters in the same order."
            )
        sent += 1
        yield name, tensor
    if sent != len(meta):
        raise ValueError(
            f"WeightSource yielded {sent} parameters but metadata() "
            f"declared {len(meta)}; the worker is waiting for the rest."
        )

_post_send_sync()

Wait for this rank's transfer work to land before returning.

Broadcasts are only enqueued by send_weights: the unpacked path on the current stream, the packed path on the producer's own streams (which it drains itself). Waiting here lets a caller mutate parameters, or start the next step on another stream, as soon as send_weights returns, instead of silently depending on same-stream ordering. Every rank waits: a non-sender's full_tensor() gathers feed the sender's broadcast, so they must have landed before it may touch its shards.

Unlike IPC there is no cross-rank barrier here. Nothing in this backend outlives the collective it travelled in (IPC's barrier keeps shared buffers alive until every consumer has opened them), so a barrier would only add a dependency on the default process group that this backend otherwise does not have.

Source code in vllm/distributed/weight_transfer/nccl_engine.py
def _post_send_sync(self) -> None:
    """Wait for this rank's transfer work to land before returning.

    Broadcasts are only *enqueued* by `send_weights`: the unpacked path on
    the current stream, the packed path on the producer's own streams (which
    it drains itself). Waiting here lets a caller mutate parameters, or
    start the next step on another stream, as soon as `send_weights`
    returns, instead of silently depending on same-stream ordering. Every
    rank waits: a non-sender's `full_tensor()` gathers feed the sender's
    broadcast, so they must have landed before it may touch its shards.

    Unlike IPC there is no cross-rank barrier here. Nothing in this backend
    outlives the collective it travelled in (IPC's barrier keeps shared
    buffers alive until every consumer has opened them), so a barrier would
    only add a dependency on the default process group that this backend
    otherwise does not have.
    """
    if torch.cuda.is_available():
        torch.cuda.current_stream().synchronize()

NCCLWeightTransferEngine

Bases: WeightTransferEngine[NCCLWeightTransferInitInfo, NCCLWeightTransferUpdateInfo]

Weight transfer engine using NCCL for communication between trainer and workers.

This implementation uses NCCL broadcast operations to transfer dense checkpoint-format weights from the trainer (rank 0) to all inference workers in a process group. Received weights are loaded via the model's load_weights using the layerwise reload lifecycle.

Methods:

Source code in vllm/distributed/weight_transfer/nccl_engine.py
class NCCLWeightTransferEngine(
    WeightTransferEngine[NCCLWeightTransferInitInfo, NCCLWeightTransferUpdateInfo]
):
    """
    Weight transfer engine using NCCL for communication between trainer and workers.

    This implementation uses NCCL broadcast operations to transfer dense
    checkpoint-format weights from the trainer (rank 0) to all inference workers
    in a process group. Received weights are loaded via the model's
    `load_weights` using the layerwise reload lifecycle.
    """

    # Define backend-specific dataclass types
    init_info_cls = NCCLWeightTransferInitInfo
    update_info_cls = NCCLWeightTransferUpdateInfo

    def __init__(
        self,
        config: WeightTransferConfig,
        vllm_config: "VllmConfig",
        device: torch.device,
        model: torch.nn.Module,
    ) -> None:
        super().__init__(config, vllm_config, device, model)
        self.model_update_group: PyNcclCommunicator | None = None
        # Set from the trainer-supplied init info at the handshake; defaults are
        # only for the (unreachable) receive-before-init case.
        self.packed = False
        self.packed_buffer_size_bytes = DEFAULT_PACKED_BUFFER_SIZE_BYTES
        self.packed_num_buffers = DEFAULT_PACKED_NUM_BUFFERS

    def init_transfer_engine(self, init_info: NCCLWeightTransferInitInfo) -> None:
        """
        Initialize NCCL process group with the trainer and record the
        trainer-supplied wire params so the worker decodes exactly as the
        trainer encodes.

        Args:
            init_info: NCCL initialization info containing master address, port,
                      rank offset, world size, and the packed wire params
        """
        self.packed = init_info.packed
        self.packed_buffer_size_bytes = init_info.packed_buffer_size_bytes
        self.packed_num_buffers = init_info.packed_num_buffers
        self.model_update_group = worker_init_process_group(
            init_info, self.parallel_config
        )

    def start_weight_update(self) -> None:
        """Initialize layerwise reloading for the incoming checkpoint weights."""
        from vllm.model_executor.model_loader.reload import (
            initialize_layerwise_reload,
        )

        initialize_layerwise_reload(self.model)

    def finish_weight_update(self) -> None:
        """Finalize layerwise reloading after all weights have been received."""
        from vllm.model_executor.model_loader.reload import (
            finalize_layerwise_reload,
        )

        finalize_layerwise_reload(self.model, self.model_config)

    def receive_weights(self, update_info: NCCLWeightTransferUpdateInfo) -> None:
        """
        Receive weights from trainer via NCCL broadcast.

        Whether to use packed broadcasting (and the buffer geometry) is read
        from `self.packed` / `self.packed_*`, set at the init handshake from the
        trainer's init info, so it is guaranteed to match how the trainer
        encoded.

        Args:
            update_info: NCCL update info containing parameter names, dtypes,
                        and shapes
        """
        if self.model_update_group is None:
            raise RuntimeError(
                "NCCL weight transfer not initialized. "
                "Call init_transfer_engine() first."
            )

        from vllm.model_executor.model_loader.mtp_validation import (
            disable_mtp_completeness_check,
        )

        with disable_mtp_completeness_check():
            if self.packed:
                # Build iterator of (name, (shape, dtype)) from update_info
                def state_dict_info_iterator():
                    for name, dtype_name, shape in zip(
                        update_info.names, update_info.dtype_names, update_info.shapes
                    ):
                        dtype = getattr(torch, dtype_name)
                        yield (name, (shape, dtype))

                packed_nccl_broadcast_consumer(
                    iterator=state_dict_info_iterator(),
                    group=self.model_update_group,
                    src=0,
                    post_unpack_func=self.model.load_weights,
                    buffer_size_bytes=self.packed_buffer_size_bytes,
                    num_buffers=self.packed_num_buffers,
                    device=self.device,
                )
            else:
                # Use simple one-by-one broadcasting
                for name, dtype_name, shape in zip(
                    update_info.names, update_info.dtype_names, update_info.shapes
                ):
                    dtype = getattr(torch, dtype_name)
                    weight = torch.empty(shape, dtype=dtype, device=self.device)
                    self.model_update_group.broadcast(
                        weight, src=0, stream=torch.cuda.current_stream()
                    )
                    self.model.load_weights([(name, weight)])
                    del weight

    def shutdown(self) -> None:
        if self.model_update_group is not None:
            # Clean up the communicator by removing the reference
            self.model_update_group = None

finish_weight_update()

Finalize layerwise reloading after all weights have been received.

Source code in vllm/distributed/weight_transfer/nccl_engine.py
def finish_weight_update(self) -> None:
    """Finalize layerwise reloading after all weights have been received."""
    from vllm.model_executor.model_loader.reload import (
        finalize_layerwise_reload,
    )

    finalize_layerwise_reload(self.model, self.model_config)

init_transfer_engine(init_info)

Initialize NCCL process group with the trainer and record the trainer-supplied wire params so the worker decodes exactly as the trainer encodes.

Parameters:

  • init_info

    (NCCLWeightTransferInitInfo) –

    NCCL initialization info containing master address, port, rank offset, world size, and the packed wire params

Source code in vllm/distributed/weight_transfer/nccl_engine.py
def init_transfer_engine(self, init_info: NCCLWeightTransferInitInfo) -> None:
    """
    Initialize NCCL process group with the trainer and record the
    trainer-supplied wire params so the worker decodes exactly as the
    trainer encodes.

    Args:
        init_info: NCCL initialization info containing master address, port,
                  rank offset, world size, and the packed wire params
    """
    self.packed = init_info.packed
    self.packed_buffer_size_bytes = init_info.packed_buffer_size_bytes
    self.packed_num_buffers = init_info.packed_num_buffers
    self.model_update_group = worker_init_process_group(
        init_info, self.parallel_config
    )

receive_weights(update_info)

Receive weights from trainer via NCCL broadcast.

Whether to use packed broadcasting (and the buffer geometry) is read from self.packed / self.packed_*, set at the init handshake from the trainer's init info, so it is guaranteed to match how the trainer encoded.

Parameters:

Source code in vllm/distributed/weight_transfer/nccl_engine.py
def receive_weights(self, update_info: NCCLWeightTransferUpdateInfo) -> None:
    """
    Receive weights from trainer via NCCL broadcast.

    Whether to use packed broadcasting (and the buffer geometry) is read
    from `self.packed` / `self.packed_*`, set at the init handshake from the
    trainer's init info, so it is guaranteed to match how the trainer
    encoded.

    Args:
        update_info: NCCL update info containing parameter names, dtypes,
                    and shapes
    """
    if self.model_update_group is None:
        raise RuntimeError(
            "NCCL weight transfer not initialized. "
            "Call init_transfer_engine() first."
        )

    from vllm.model_executor.model_loader.mtp_validation import (
        disable_mtp_completeness_check,
    )

    with disable_mtp_completeness_check():
        if self.packed:
            # Build iterator of (name, (shape, dtype)) from update_info
            def state_dict_info_iterator():
                for name, dtype_name, shape in zip(
                    update_info.names, update_info.dtype_names, update_info.shapes
                ):
                    dtype = getattr(torch, dtype_name)
                    yield (name, (shape, dtype))

            packed_nccl_broadcast_consumer(
                iterator=state_dict_info_iterator(),
                group=self.model_update_group,
                src=0,
                post_unpack_func=self.model.load_weights,
                buffer_size_bytes=self.packed_buffer_size_bytes,
                num_buffers=self.packed_num_buffers,
                device=self.device,
            )
        else:
            # Use simple one-by-one broadcasting
            for name, dtype_name, shape in zip(
                update_info.names, update_info.dtype_names, update_info.shapes
            ):
                dtype = getattr(torch, dtype_name)
                weight = torch.empty(shape, dtype=dtype, device=self.device)
                self.model_update_group.broadcast(
                    weight, src=0, stream=torch.cuda.current_stream()
                )
                self.model.load_weights([(name, weight)])
                del weight

start_weight_update()

Initialize layerwise reloading for the incoming checkpoint weights.

Source code in vllm/distributed/weight_transfer/nccl_engine.py
def start_weight_update(self) -> None:
    """Initialize layerwise reloading for the incoming checkpoint weights."""
    from vllm.model_executor.model_loader.reload import (
        initialize_layerwise_reload,
    )

    initialize_layerwise_reload(self.model)

NCCLWeightTransferInitInfo dataclass

Bases: WeightTransferInitInfo

Worker-side initialization info for NCCL-based weight transfer backends.

Source code in vllm/distributed/weight_transfer/nccl_common.py
@dataclass
class NCCLWeightTransferInitInfo(WeightTransferInitInfo):
    """Worker-side initialization info for NCCL-based weight transfer backends."""

    master_address: str
    master_port: int
    rank_offset: int
    world_size: int
    packed: bool = False
    packed_buffer_size_bytes: int = DEFAULT_PACKED_BUFFER_SIZE_BYTES
    packed_num_buffers: int = DEFAULT_PACKED_NUM_BUFFERS

NCCLWeightTransferUpdateInfo dataclass

Bases: WeightTransferUpdateInfo

Per-round update info for the dense NCCL weight transfer backend.

Whether the transfer is packed (and the buffer geometry) is a must-agree wire param carried on the init info (NCCLTrainerInitInfo / NCCLWeightTransferInitInfo), not here; this carries only the per-round parameter metadata.

Methods:

  • __post_init__

    Validate that all lists have the same length.

Source code in vllm/distributed/weight_transfer/nccl_engine.py
@dataclass
class NCCLWeightTransferUpdateInfo(WeightTransferUpdateInfo):
    """Per-round update info for the dense NCCL weight transfer backend.

    Whether the transfer is packed (and the buffer geometry) is a must-agree
    wire param carried on the init info (`NCCLTrainerInitInfo` /
    `NCCLWeightTransferInitInfo`), not here; this carries only the per-round
    parameter metadata.
    """

    names: list[str]
    dtype_names: list[str]
    shapes: list[list[int]]

    def __post_init__(self):
        """Validate that all lists have the same length."""
        num_params = len(self.names)
        if len(self.dtype_names) != num_params:
            raise ValueError(
                f"`dtype_names` should be of the same size as `names`: "
                f"got {len(self.dtype_names)} and {len(self.names)}"
            )
        if len(self.shapes) != num_params:
            raise ValueError(
                f"`shapes` should be of the same size as `names`: "
                f"got {len(self.shapes)} and {len(self.names)}"
            )

__post_init__()

Validate that all lists have the same length.

Source code in vllm/distributed/weight_transfer/nccl_engine.py
def __post_init__(self):
    """Validate that all lists have the same length."""
    num_params = len(self.names)
    if len(self.dtype_names) != num_params:
        raise ValueError(
            f"`dtype_names` should be of the same size as `names`: "
            f"got {len(self.dtype_names)} and {len(self.names)}"
        )
    if len(self.shapes) != num_params:
        raise ValueError(
            f"`shapes` should be of the same size as `names`: "
            f"got {len(self.shapes)} and {len(self.names)}"
        )