Skip to content

[RL] P2P RDT weight sync - #43375

Open
hao-aaron wants to merge 83 commits into
vllm-project:mainfrom
hao-aaron:rdt-weight-sync
Open

[RL] P2P RDT weight sync#43375
hao-aaron wants to merge 83 commits into
vllm-project:mainfrom
hao-aaron:rdt-weight-sync

Conversation

@hao-aaron

@hao-aaron hao-aaron commented May 22, 2026

Copy link
Copy Markdown
Contributor

original design doc: https://docs.google.com/document/d/103ppQh3VxOR6Njzw0PQn6vGIaDFOAxTpiS9_Na68zN4/edit?tab=t.0

Summary

A fourth weight-transfer backend, sharded_rdt. Every existing backend pushes
whole parameters to every inference worker. This one lets each worker pull
only the slice it actually consumes
under tensor and expert parallelism, over
NIXL / Ray Direct Transport. A large MoE moves roughly total_bytes / num_workers
per worker instead of total_bytes.

The mechanism, in one paragraph: at init we run model.load_weights once against
zero-storage placeholder tensors and record what each of vLLM's own weight
loaders does
narrow, t, view, … — as a replayable op chain per
destination slice. Every later sync is pure replay: the worker asks a trainer rank
for exactly those slices, and they land directly in the
layerwise reload buffers.
No full HF tensor is ever materialized on a worker.

Measured on Qwen3-235B-A22B, 8 trainer GPUs → 8 inference GPUs (H100): the full
472 GB moves in a 3.3 s warm sync under DP8+EP8 at 43 GiB/s, vs 6.1 s at
26–28 GiB/s for the same model under TP8. GLM-4.5-Air holds the same wire rate;
Kimi K2 (1T, FP8) is validated from a raw sharded checkpoint. Full table below.

# File Lines What it is
1 docs/training/weight_transfer/sharded_rdt.md 126 Start here. The whole design in a page
2 vllm/distributed/weight_transfer/base.py +154 The three ABC additions this backend needs
3 .../sharded_rdt_lazy.py 300 The recorder: how a loader's slicing becomes a wire format
4 .../sharded_rdt_common.py 225 The op allowlist and the routing rules. No I/O, no Ray
5 .../sharded_rdt_engine.py 1404 Consumer side: bake, plan, pull, scatter
6 .../sharded_rdt_trainer.py 1053 Trainer side: ownership, gather loop, the serve actor
7 tests/distributed/test_sharded_rdt_*.py 2595 142 tests, all CPU/meta
8 examples/rl/*rdt*.py 972 Two runnable 2-node examples + 243 lines of shared driver helpers

How it works

1. Slices are tracked through vLLM's own weight loaders

A weight loader normally receives a full HF tensor and slices out this worker's
part. We hand it a LazyRDTTensor instead: a _make_wrapper_subclass tensor that
answers .shape / .dtype / .size() but owns no storage. Every allowlisted
view or shape op returns a new lazy with that op appended to a recorded chain,
and copy_ is the sink that terminates it.

The chain is the wire format:

("model.layers.0.w", (("narrow", (0, 512, 512), ()), ("t", (), ())))

The trainer replays it as getattr(tensor, op)(*args, **kwargs) against its live
parameter and sends the result.

Anything a loader does that needs real data — arithmetic, .to(), .item(),
.data, bool-mask indexing — falls off the allowlist, reaches
__torch_dispatch__, and raises at init. That is the intended behavior:
failing during setup beats silently transferring the wrong bytes. SUPPORTED_OPS
is a single table both sides derive from, so the recorder and the replayer cannot
drift apart.

2. Discovery happens once

Running the loaders is expensive, so it happens exactly once, at
init_transfer_engine, as a dry run with every parameter on meta. Nothing
transfers; we only record, per leaf module, which slice feeds which
as_strided destination region. Every later sync is replay with no
load_weights, no lazy dispatch, no discovery.

A module only contributes its recording if it fully loaded (copied numel ≥
get_layer_size). A partially-recorded module would leave unwritten regions for
finalize to initialize, so baking it would scatter garbage.

There is no fallback path. A name whose copy_ fired but which produced no
recording fails the plan build at init, naming the offending weights. Earlier
revisions carried a per-slice fallback load; it was deleted (see
Removed before review).

3. Received slices land directly in the layerwise reload buffers

The engine drives layerwise reload itself, in start_weight_update /
finish_weight_update. Because each destination is already recorded as an
as_strided region, an arriving slice is copied straight into the layer being
reloaded. Each layer is quantized and copied into its persistent kernel storage as
soon as its last slice lands, on a dedicated stream, so quant overlaps the next
layer's transfer.

4. Gathers and pulls are pipelined

The trainer usually cannot serve its parameters as they sit: FSDP shards them, and
even an EP-split trainer has to assemble a whole expert. So each sync still runs
gather collectives — but a layer at a time.

A gather group is one decoder layer (the name list is cut on
model.layers.<N>. boundaries, leaving the embeddings and the final norm /
lm_head as one group each). The trainer gathers a layer, publishes it —
immediately pullable — and moves on while the consumers pull the previous one.
When every consumer signals it is done with a layer, the trainer drops it and
gains a credit to gather another.

gather_lookahead bounds that: at most gather_lookahead + 1 layers are resident
on the trainer. The default of 1 keeps the next layer gathered and serveable
while the current one is being pulled, which hides the handoff at the 2-layer
memory floor.

Gating publishes instead of gathers was tried and is worse both ways: it
holds layer N+1 gathered but unserveable across every boundary (~2.5–3 s of
235B sync wall) and keeps more layers resident.

5. A trainer rank need not hold the whole model

Each rank declares what it holds via WeightSource.held_names(); the fleet
all-gathers those declarations at trainer_init and transposes them into a
compact table (the distinct owner sets, plus a per-name index into them). Consumers
route each pull to a rank that actually holds the name.

One declaration expresses every layout — pipeline stages, expert parallelism,
both at once, or a shape that fits neither. Consumers block-and-rotate across the
ranks that hold a name so no single trainer NIC becomes the bottleneck.


Public API

Two new entries in existing registries and one new init-info dataclass. Nothing
else is added to the public surface.

Inference side — a plain backend selector; everything else arrives from the
trainer at the init handshake:

llm = LLM(
    model="my-model",
    weight_transfer_config=WeightTransferConfig(backend="sharded_rdt"),
    distributed_executor_backend="ray",
)

Trainer side — identical in shape to the NCCL and IPC engines:

engine = WeightTransferTrainerFactory.trainer_init(
    init_info=ShardedRDTTrainerInitInfo(
        rank=rank,                                # 0 is the sender
        num_consumers=8,                          # inference workers, fleet-wide
        trainer_actor_namespace="my_namespace",
    ),
    client=HTTPVLLMWeightSyncClient("http://localhost:8000"),
    source=ModuleSource(model),
)
engine.send_weights()   # once per sync, on every trainer rank

Requirements

  • distributed_executor_backend="ray" — workers must be Ray actors
  • nixl installed in the environment shared by trainer and workers
  • A fabric NIXL supports (InfiniBand, RoCE, EFA)
  • Weight loaders that stay inside SUPPORTED_OPS
  • EPLB is rejected at init: runtime expert rearrangement invalidates the
    recorded plan, and a silent mismatch would load weights into the wrong expert
    slots

Shared code touched

Four files outside the new backend. All additive; no existing backend changes
behavior.

vllm/distributed/weight_transfer/base.py (+154) — three additions to the
ABCs, each with a working default so existing sources and engines are unaffected:

  • layerwise_groups(names) — the pre / per-decoder-layer / post partition. It
    lives in base.py rather than in the backend because it defines what a group
    index
    means for any WeightSource.
  • WeightSource.held_names()Collection[str] | None, defaulting to None
    ("this rank holds everything"), plus groups() and iter_groups() derived from
    it. iter_groups() also exists for cost: driving materialization per group
    instead of per tensor turns ~37k generator resumes into ~95 on a per-expert MoE.
  • WeightTransferEngine.defers_processing (default False) and
    drain_pending() (default no-op). This engine pipelines its GPU
    post-processing onto background threads, so update_weights cannot synchronize
    the device; the flag tells a caller that has taken over the update tail that it
    must drain first.

vllm/v1/executor/ray_executor_v2.py (+9) — Ray requires the calling actor
to opt into tensor transport, so worker actors get
enable_tensor_transport=True when and only when the backend is sharded_rdt.

vllm/config/weight_transfer.py (+1/−1) and factory.py (+12) — the
backend literal and the two lazy factory registrations, matching the existing
pattern exactly.

tools/pre_commit/check_torch_cuda.py (+1) — allowlists
sharded_rdt_engine.py, which needs real torch.cuda streams and events for the
receive-slot handshake (same exemption ipc_engine.py already has).


Tests

142 tests across three new files, all CPU/meta — no GPU, no Ray, no NIXL. The
planning core is pure (baked plan + partition → static plan), which is what makes
that possible.

tests/distributed/test_sharded_rdt_plan.py       77 tests   consumer: recording, routing, planning, packed layout
tests/distributed/test_sharded_rdt_trainer.py    24 tests   trainer: ownership resolution, send_weights round trip
tests/distributed/test_sharded_rdt_producer.py   41 tests   the serve actor's protocol: free barrier, credits, watchdog
tests/distributed/test_weight_transfer.py       +13 tests   the two new ABC contracts, across all backends

Three of these are load-bearing rather than routine:

  • TestPackedLayout guards the one invariant no runtime check can catch. Both
    sides compute the packed byte layout independently; if they disagree the bytes
    still arrive exactly as sent and only the carving differs, so weights are
    silently wrong. The test transcribes the producer's rule independently and
    asserts the consumer's matches, on a mixed-dtype group whose sizes do not land
    on a 16 B boundary.
  • TestSignalCompleteness pins that every gather group is signalled exactly
    once per sync, under every chunking shape. A missed signal wedges the trainer's
    gather loop; a doubled one frees a layer while a pull is still reading it.
  • TestWeightSourceGroupContract pins the new ABC defaults against a source
    that holds only part of the model.

Commands run

python -m pytest tests/distributed/test_sharded_rdt_plan.py \
                 tests/distributed/test_sharded_rdt_trainer.py \
                 tests/distributed/test_sharded_rdt_producer.py \
                 tests/distributed/test_weight_transfer.py -q
# 223 passed, 26 skipped in 73s

pre-commit run --all-files
# all hooks pass (ruff-check, ruff-format, typos, markdownlint-cli2,
#                 mypy-3.10, check-spdx-header, check_torch_cuda)

Of the 26 skips, 7 belong to this PR — the trainer-side tests that need a real
device (CUDA-IPC export in the gather loop, serve-ring packing). They pass on a
GPU host. The other 19 are pre-existing GPU skips in test_weight_transfer.py for
the NCCL, IPC and sparse backends.

Worth noting what does not skip: all 77 consumer-plan tests and all 41
producer-protocol tests run on CPU, because the planning core is pure and the
serve actor is exercised through a local (non-Ray) instance.

End-to-end validation

Both examples were run on 2 nodes × 8×H100:

Model Trainer Inference Result
Qwen3-30B-A3B 8-rank FSDP2 DP8 + EP Correct generations after sync; 3 consecutive syncs
Qwen3-235B-A22B 8-rank FSDP2 DP8 + EP8 36945/36945 names baked on 72/72 workers; 3.2–3.3 s/sync
Kimi K2 (1T, FP8) 8 ranks, raw sharded ckpt 8 GPUs Correct generations; custom WeightSource, no HF materialization

Model evaluation: this is a weight transport, not a modeling change — the
bytes that land are the trainer's parameters, and correctness is "the inference
engine produces the trainer's model." That is verified end to end in the examples
(coherent generations after sync vs. garbage from dummy weights before it) and at
the byte level by TestPackedLayout. No accuracy-affecting kernel or model code
is touched, so tests/evals/ was not run.


Performance

8×H100 trainer → 8×H100 inference, measured at the engine level:

Qwen3-235B (tp8) Qwen3-235B (dp8/ep8) GLM-4.5-Air (dp8/ep8)
Model / sync 472 GB 472 GB 214 GB
Pulls per consumer 848 190 92
Wire rate 26–28 GiB/s 43 GiB/s 42 GiB/s
Warm engine wall 6.1 s 3.3 s ~2.2 s

The two Qwen3-235B columns move the same 472 GB — the difference is entirely in
how it is cut. Under DP+EP each consumer holds full experts for exactly one
trainer EP coordinate, so it pulls ~2 slices per layer (one expert plus one
replicated) instead of a TP slice of every coordinate. Four times fewer, larger
pulls is what takes the wire rate from 26–28 GiB/s to 43: the transfer stops being
per-slice latency-bound and starts being bandwidth-bound, which is the whole point
of the packed-chunk design.

GLM-4.5-Air is the same shape at smaller scale and lands at the same wire rate,
which is the useful signal — 42 GiB/s is the fabric, not a model-specific result.

The bill on 80 GB cards is real and worth stating: weights are 67.8 GiB/rank under
DP+EP (vs ~59 under TP8, since attention and embeddings are unsliced), the DP+EP
fused-MoE workspace scales with max_num_batched_tokens × dp_size, and the 2.5
GiB of receive arenas need enforce_eager to fit. Treat DP+EP at this scale as an
eager-mode configuration for now.


Known limitations

urgent follow ups:

  • NIXL buffer memory Currently, nixl buffers are sized to the largest chunk. In situations where many consumers pull from one producer, each consumer must have their own buffer, which can be very large. Also, there is much unused space inside each buffer due to sizing to largest.
  • PP stages parallel send currently we are restricted to sending on layer at a time due to layerwise constraints. This limits our maximum bandwidth, since we are bottlenecked by the throughput of one stage. However, we can send from multiple pp stages in parallel if the inference side is composed of independent replicas.

less urgent:

  • The receive arenas sit outside gpu_memory_utilization. Like NCCL and NIXL
    internals they are not counted, so a fraction that leaves no headroom OOMs at
    the first sync even though the engine came up healthy. Documented with a warning
    box; arena_presize_gb exists to size them deterministically.
  • The bake leans on layerwise-reload internals that a public API should expose
    first-class: a currently-loading hook instead of monkeypatched loader stamps, a
    dry-run mode instead of bypassing online_process_loader, and an
    abort_layerwise_reload instead of a hand-rolled restore. Marked in
    _bake's docstring; a good follow-up.
  • The producer actor's concurrency is protocol-safe, not lock-safe. Several
    shared dicts are correct only because the free barrier orders access. It has not
    bitten, but a change to free timing or actor concurrency could expose it.
  • _all_gather_owned is a full all-gather where a gather-to-rank-0 would do,
    except that every rank retains the table so a restarted consumer can rejoin via
    get_worker_init_payload() without a collective. ~12 KB/rank at 100k names, so
    not urgent.

Documentation

  • docs/training/weight_transfer/sharded_rdt.md (new) — the backend page,
    matching the structure of nccl.md and ipc.md.
  • docs/training/weight_transfer/base.md (+114) — the WeightSource section
    is reframed as the adapter for whatever shape your trainer's weights are in,
    and now documents held_names() and gather groups. The example is a
    Megatron-Bridge source, since that is the shape framework authors actually start
    from.
  • README.md — the backend table.

AI assistance

This change was developed with AI assistance (Claude). The design decisions,
the measured results, the multi-node validation runs, and every line of the diff
were reviewed by the submitting human, who can defend the change end to end.

hao-aaron added 2 commits May 22, 2026 00:58
x
Signed-off-by: ahao-anyscale <ahao@anyscale.com>
x
Signed-off-by: ahao-anyscale <ahao@anyscale.com>
@mergify

mergify Bot commented May 22, 2026

Copy link
Copy Markdown
Contributor

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

@mergify mergify Bot added documentation Improvements or additions to documentation v1 labels May 22, 2026

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces the Ray Direct Transport (RDT) weight transfer backend, which allows vLLM inference workers to pull weights directly from a trainer Ray actor using NIXL. The implementation includes the new RDTWeightTransferEngine, its registration in the factory, and updates to the Ray executor to support tensor transport. Additionally, two example scripts demonstrate standard and elastic RL scaling using this backend. Review feedback suggested several enhancements for robustness and performance, including enforcing the Ray executor backend requirement, safely handling private Ray attributes, improving error messages for missing producer methods, and implementing a fetch-ahead mechanism to overlap weight transfers with loading.

Comment thread vllm/distributed/weight_transfer/rdt_engine.py Outdated
Comment thread vllm/distributed/weight_transfer/rdt_engine.py Outdated
Comment thread vllm/distributed/weight_transfer/rdt_engine.py Outdated
Comment thread vllm/distributed/weight_transfer/rdt_engine.py Outdated
hao-aaron added 2 commits May 28, 2026 05:23
x
Signed-off-by: hao-aaron <ahao@anyscale.com>
x
Signed-off-by: hao-aaron <ahao@anyscale.com>
Signed-off-by: hao-aaron <ahao@anyscale.com>
@mergify mergify Bot added the qwen Related to Qwen models label Jun 3, 2026
hao-aaron added 2 commits June 3, 2026 22:54
Signed-off-by: hao-aaron <ahao@anyscale.com>
Signed-off-by: hao-aaron <ahao@anyscale.com>
@mergify

mergify Bot commented Jun 4, 2026

Copy link
Copy Markdown
Contributor

This pull request has merge conflicts that must be resolved before it can be
merged. Please rebase the PR, @hao-aaron.

https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/syncing-a-fork

@mergify mergify Bot added the needs-rebase label Jun 4, 2026
hao-aaron added 2 commits June 4, 2026 21:42
Signed-off-by: hao-aaron <ahao@anyscale.com>
Signed-off-by: hao-aaron <ahao@anyscale.com>
Signed-off-by: haoaaron <ahao@anyscale.com>
Signed-off-by: haoaaron <ahao@anyscale.com>
x
Signed-off-by: haoaaron <ahao@anyscale.com>
Signed-off-by: haoaaron <ahao@anyscale.com>
Signed-off-by: haoaaron <ahao@anyscale.com>
Signed-off-by: haoaaron <ahao@anyscale.com>
Signed-off-by: haoaaron <ahao@anyscale.com>
Signed-off-by: haoaaron <ahao@anyscale.com>
Signed-off-by: haoaaron <ahao@anyscale.com>
Signed-off-by: haoaaron <ahao@anyscale.com>
Signed-off-by: haoaaron <ahao@anyscale.com>
@mergify mergify Bot added the ci/build label Aug 20, 2026
@mergify

mergify Bot commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

This pull request has merge conflicts that must be resolved before it can be
merged. Please rebase the PR, @hao-aaron.

https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/syncing-a-fork

@mergify mergify Bot added the needs-rebase label Aug 20, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The number of buffers here is not documented properly:

It's 2 buffers per gather for the trainer ranks and then 2 buffers per consumer in the serve producer by default. Controlled by DEFAULT_GATHER_LOOKAHEAD and num_rdt_buffers

I would want a clear description of this in the docs, probably in limitations. We should also mention future plans to migrate to remote GPU -> CPU memory transfer since that was just added: ray-project/ray#64815

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's add this after you add your optimization of using only one common buffer for all consumers

@SumanthRH

Copy link
Copy Markdown
Contributor

/ci run

@github-actions

Copy link
Copy Markdown

@SumanthRH, Only reviewers with write access can use CI commands before CI is delegated to the PR author.

Signed-off-by: haoaaron <ahao@anyscale.com>
Comment on lines +509 to +523
# ``ray.get_actor`` resolves by NAME, in a process that never
# imported the producer's class, and ``enable_tensor_transport``
# lives in that class's metadata: the creating process passes
# ``meta.enable_tensor_transport``, and Ray infers it from the
# class's ``@ray.method(tensor_transport=...)`` decorators. A
# name-resolved handle therefore always reports False and the
# dispatch guard rejects the pull, even though the trainer set the
# option. Inherent to resolving by name across processes, not a
# version bug; it fails on Ray 2.56.0.
#
# Forcing it skips no validation. Ray's next guard,
# ``actor_has_tensor_transport``, asks the LIVE actor whether it can
# build a NIXL agent and still runs, and ``_spawn_server`` hardcodes
# the actor option, so a misconfigured producer cannot reach here.
actor._ray_enable_tensor_transport = True

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In summary:

This code gets the actor handle for the trainer by resolving by actor name;

actor = ray.get_actor(
                    chosen_name,
                    namespace=init_info.trainer_actor_namespace,
                )

However, the _ray_enable_tensor_transport attribute is not set on the actor. Seems like a limitation of how we resolve actors with RDT right now @Sparks0219

Comment on lines +91 to +120
# Cross-deployment serve-slot sharing.
#
# Consumers whose ids differ by a multiple of ``workers_per_replica`` are the
# same worker of different inference deployments: identical parallel config,
# identical baked plan, identical chunk sequence, byte-identical pack layout. A
# serve ring per consumer therefore costs one full ring per deployment on the
# producer's GPU, and repeats the pack once per deployment for identical bytes.
#
# NIXL reads are one-sided and non-destructive, so R readers can read ONE
# registered slot concurrently. What a producer cannot observe is when a reader
# has FINISHED, so the release edge comes from the consumer's ISSUE order, which
# it sends as ``seq``: the pipeline drains pull i before issuing i+K, so slot
# ``seq % K`` was last packed for ``seq - K``, whose read is over. Under sharing
# the same holds, because generation ``seq`` is only packed once every live
# sharer has arrived at it, so each has drained ``seq - K``.
#
# Deriving the slot from a per-call counter on THIS side instead (execution
# order) is wrong: Ray may start a consumer's K concurrent produce calls in any
# order, so the call that executes K-before another can be a pull that is still
# being read, and its slot gets repacked underneath the reader. Silent, showing
# up only as a logprob drift.
#
# Hence one rendezvous per generation, keyed by ``seq``: the group's live sharers
# meet there, the LAST to arrive packs, and all of them return that one blob.
#
# Sharers that do NOT pull the same chunks would rendezvous on sequences whose
# bytes differ, so the plan is compared at init instead
# (``reserve_serve_buffer``'s ``plan_digest``), where it is one error naming both
# consumers before any byte moves. A sharer that dies mid-sync stalls its group,
# exactly as a dead consumer already stalls the per-group free barrier; the next

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we also add a note here on the limitation of cross deployment slot sharing here? We are introducing some synchronization here across replicas to make this word. Ideally in P2P we don't have any such synchronization, but this is a limitation given the current model of GPU -> GPU memory transfers with RDT with limited buffer sizes on GPU.

Let's add a TODO to explore GPU -> CPU memory transfer that could eliminate this

hao-aaron and others added 2 commits August 21, 2026 11:10
Upstream vllm-project/vllm main at 7a2fdba (325 commits since
b652ded). Two conflicts, both in files the RDT work also touches:

- .buildkite/test_areas/distributed.yaml: upstream relabelled every step
  to ":nvidia: (<gpu>) <name>". Kept the Sharded RDT step and renamed it
  to match.
- docs/training/weight_transfer/README.md: auto-merged; upstream's new
  Rust-frontend paragraph listed the transports, so sharded-RDT was added
  to that list.

Nothing the RDT code imports moved: of its upstream dependencies only
config/parallel.py and v1/executor/multiproc_executor.py changed in the
range, and world_size_across_dp / FutureWrapper / MultiprocExecutor /
WorkerProc are all still there.

Tests: PYTHONPATH=<fork> pytest tests/distributed/test_sharded_rdt_{producer,plan,trainer}.py
--noconftest -> 202 passed, 10 skipped (the documented baseline).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: haoaaron <ahao@anyscale.com>
@mergify mergify Bot removed the needs-rebase label Aug 21, 2026
@hao-aaron

Copy link
Copy Markdown
Contributor Author

/ci run

@github-actions

Copy link
Copy Markdown

✅ Triggered Buildkite CI #85072 for commit fb33e27ba5c7.

Comment on lines +554 to +558
rings = self._serve_rings.setdefault(sg, [None] * self._nring)
for i in range(self._nring):
slot = rings[i]
if slot is None or slot.numel() < alloc:
rings[i] = self._new_serve_buffer(alloc)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wait, so we still have one serve ring per consumer per trainer rank?

Your current fix ensures that we don't scale with number of replicas, but it will still scale with the number of workers per replica?

Signed-off-by: haoaaron <ahao@anyscale.com>
@SumanthRH

Copy link
Copy Markdown
Contributor

There are some CI failures @hao-aaron . Can you take a look? Was the test written for L4s?

2026-08-21T19:42:25Z] distributed/test_sharded_rdt_plan.py::TestBakeOnARealModel::test_bake_records_a_replayable_plan_and_restores_the_model[mla-moe] �[32mINFO�[0m �[90m08-21 19:42:25�[0m �[90m[api_utils.py:286]�[0m non-default args: {'trust_remote_code': True, 'load_format': 'dummy', 'max_model_len': 1024, 'block_size': 16, 'disable_log_stats': True, 'enforce_eager': True, 'enable_chunked_prefill': False, 'compilation_config': {'mode': None, 'debug_dump_path': None, 'cache_dir': '', 'compile_cache_save_format': 'binary', 'backend': 'inductor', 'custom_ops': [], 'ir_enable_torch_wrap': None, 'splitting_ops': None, 'compile_mm_encoder': False, 'cudagraph_mm_encoder': False, 'encoder_cudagraph_token_budgets': [], 'encoder_cudagraph_max_vision_items_per_batch': 0, 'encoder_cudagraph_max_frames_per_batch': None, 'compile_sizes': None, 'compile_ranges_endpoints': None, 'inductor_compile_config': {'enable_auto_functionalized_v2': False, 'combo_kernels': True, 'benchmark_combo_kernel': True}, 'inductor_passes': {}, 'cudagraph_mode': None, 'cudagraph_num_of_warmups': 0, 'cudagraph_capture_sizes': [4], 'cudagraph_copy_inputs': False, 'cudagraph_specialize_lora': True, 'use_inductor_graph_partition': None, 'pass_config': {}, 'max_cudagraph_capture_size': None, 'dynamic_shapes_config': {'type': <DynamicShapesType.BACKED: 'backed'>, 'evaluate_guards': False, 'assume_32_bit_indexing': False}, 'local_cache_dir': None, 'fast_moe_cold_start': None, 'static_all_moe_layers': []}, 'model': 'deepseek-ai/DeepSeek-V2-Lite'}
[2026-08-21T19:42:25Z] �[33mWARNING�[0m �[90m08-21 19:42:25�[0m �[90m[envs.py:2242]�[0m Unknown vLLM environment variable detected: VLLM_CI_TRIGGERED_BY
[2026-08-21T19:42:25Z] �[33mWARNING�[0m �[90m08-21 19:42:25�[0m �[90m[envs.py:2242]�[0m Unknown vLLM environment variable detected: VLLM_CI_GITHUB_COMMENT_ID
[2026-08-21T19:42:35Z] �[32mINFO�[0m �[90m08-21 19:42:35�[0m �[90m[model.py:684]�[0m Resolved architecture: DeepseekV2ForCausalLM
[2026-08-21T19:42:35Z] �[32mINFO�[0m �[90m08-21 19:42:35�[0m �[90m[model.py:2017]�[0m Using max model len 1024
[2026-08-21T19:42:35Z] �[32mINFO�[0m �[90m08-21 19:42:35�[0m �[90m[kernel.py:310]�[0m Final IR op priority after setting platform defaults: IrOpPriorityConfig(rms_norm=['vllm_c', 'native'], fused_add_rms_norm=['vllm_c', 'native'])
[2026-08-21T19:42:37Z] �[0;36m(EngineCore pid=522)�[0;0m �[32mINFO�[0m �[90m08-21 19:42:37�[0m �[90m[core.py:122]�[0m Initializing a V1 LLM engine (v0.26.1rc1.dev1157+gfb33e27ba) with config: model='deepseek-ai/DeepSeek-V2-Lite', speculative_config=None, tokenizer='deepseek-ai/DeepSeek-V2-Lite', skip_tokenizer_init=False, tokenizer_mode=auto, revision=main, tokenizer_revision=main, trust_remote_code=True, dtype=torch.bfloat16, max_seq_len=1024, download_dir=None, load_format=dummy, tensor_parallel_size=1, pipeline_parallel_size=1, data_parallel_size=1, decode_context_parallel_size=1, dcp_comm_backend=ag_rs, disable_custom_all_reduce=False, quantization=None, quantization_config=None, enforce_eager=True, enable_return_routed_experts=False, kv_cache_dtype=auto, device_config=cuda, structured_outputs_config=StructuredOutputsConfig(backend='auto', disable_any_whitespace=False, disable_additional_properties=False, reasoning_parser='', reasoning_parser_plugin='', enable_in_reasoning=False), observability_config=ObservabilityConfig(show_hidden_metrics_for_version=None, otlp_traces_endpoint=None, collect_detailed_traces=None, per_request_spec_decode_metrics='none', kv_cache_metrics=False, kv_cache_metrics_sample=0.01, cudagraph_metrics=False, enable_layerwise_nvtx_tracing=False, enable_mfu_metrics=False, enable_mm_processor_stats=False, enable_logging_iteration_details=False, jit_monitor_mode='warn', jit_monitor_verbose=False), seed=0, served_model_name=deepseek-ai/DeepSeek-V2-Lite, enable_prefix_caching=True, enable_chunked_prefill=False, pooler_config=None, compilation_config={'mode': <CompilationMode.NONE: 0>, 'debug_dump_path': None, 'cache_dir': '', 'compile_cache_save_format': 'binary', 'backend': 'inductor', 'custom_ops': ['all'], 'ir_enable_torch_wrap': False, 'splitting_ops': [], 'compile_mm_encoder': False, 'cudagraph_mm_encoder': False, 'encoder_cudagraph_token_budgets': [], 'encoder_cudagraph_max_vision_items_per_batch': 0, 'encoder_cudagraph_max_frames_per_batch': None, 'compile_sizes': [], 'compile_ranges_endpoints': [8192], 'inductor_compile_config': {'enable_auto_functionalized_v2': False, 'combo_kernels': True, 'benchmark_combo_kernel': True}, 'inductor_passes': {}, 'cudagraph_mode': <CUDAGraphMode.NONE: 0>, 'cudagraph_num_of_warmups': 0, 'cudagraph_capture_sizes': [], 'cudagraph_copy_inputs': False, 'cudagraph_specialize_lora': True, 'use_inductor_graph_partition': False, 'pass_config': {'fuse_norm_quant': True, 'fuse_act_quant': True, 'fuse_attn_quant': False, 'enable_sp': False, 'fuse_gemm_comms': False, 'fuse_allreduce_rms': False, 'enable_qk_norm_rope_fusion': False, 'fuse_rope_kvcache_cat_mla': False, 'fuse_act_padding': False, 'fuse_qk_norm_rope_kvcache': False}, 'max_cudagraph_capture_size': 0, 'dynamic_shapes_config': {'type': <DynamicShapesType.BACKED: 'backed'>, 'evaluate_guards': False, 'assume_32_bit_indexing': False}, 'local_cache_dir': None, 'fast_moe_cold_start': False, 'static_all_moe_layers': []}, kernel_config=KernelConfig(ir_op_priority=IrOpPriorityConfig(rms_norm=['vllm_c', 'native'], fused_add_rms_norm=['vllm_c', 'native']), enable_flashinfer_autotune=True, enable_cutedsl_warmup=True, enable_jit_warmup=True, enable_bf16x3_router_gemm=False, moe_backend='auto', linear_backend='auto')
[2026-08-21T19:42:37Z] �[0;36m(EngineCore pid=522)�[0;0m �[32mINFO�[0m �[90m08-21 19:42:37�[0m �[90m[parallel_state.py:1638]�[0m world_size=1 rank=0 local_rank=0 distributed_init_method=file:///tmp/vllm_dist_2b1fb83cf33347ff9b2c7035cad4385e backend=nccl
[2026-08-21T19:42:37Z] �[0;36m(EngineCore pid=522)�[0;0m �[32mINFO�[0m �[90m08-21 19:42:37�[0m �[90m[parallel_state.py:1982]�[0m rank 0 in world size 1 is assigned as DP rank 0, PP rank 0, PCP rank 0, TP rank 0, EP rank 0, EPLB rank N/A
[2026-08-21T19:42:37Z] �[0;36m(EngineCore pid=522)�[0;0m �[32mINFO�[0m �[90m08-21 19:42:37�[0m �[90m[gpu_worker.py:396]�[0m Using V2 Model Runner
[2026-08-21T19:42:38Z] �[0;36m(EngineCore pid=522)�[0;0m �[32mINFO�[0m �[90m08-21 19:42:38�[0m �[90m[model_runner.py:368]�[0m Loading model from scratch...
[2026-08-21T19:42:39Z] �[0;36m(EngineCore pid=522)�[0;0m �[32mINFO�[0m �[90m08-21 19:42:39�[0m �[90m[cuda.py:492]�[0m Using TRITON_MLA attention backend out of potential backends: ['TRITON_MLA'].
[2026-08-21T19:42:39Z] �[0;36m(EngineCore pid=522)�[0;0m �[32mINFO�[0m �[90m08-21 19:42:39�[0m �[90m[selector.py:192]�[0m Using FLASH_ATTN MLA prefill backend.
[2026-08-21T19:42:39Z] �[0;36m(EngineCore pid=522)�[0;0m �[32mINFO�[0m �[90m08-21 19:42:39�[0m �[90m[unquantized.py:319]�[0m Using TRITON Unquantized MoE backend out of potential backends: ['FlashInfer TRTLLM', 'FlashInfer CUTLASS', 'TRITON', 'BATCHED_TRITON'].
[2026-08-21T19:42:39Z] [rank0]:[W821 19:42:39.663069685 CUDACachingAllocator.cpp:3933] memory allocation failed with OOM on device 0 while trying to allocate 738197504 bytes (free: 362938368, total: 23670685696).
[2026-08-21T19:42:39Z] [rank0]:[W821 19:42:39.680120023 CUDACachingAllocator.cpp:3933] memory allocation failed with OOM on device 0 while trying to allocate 738197504 bytes (free: 446824448, total: 23670685696).
[2026-08-21T19:42:40Z] �[0;36m(EngineCore pid=522)�[0;0m �[31mERROR�[0m �[90m08-21 19:42:40�[0m �[90m[core.py:1354]�[0m EngineCore failed to start.

[2026-08-21T19:42:40Z] �[0;36m(EngineCore pid=522)�[0;0m �[31mERROR�[0m �[90m08-21 19:42:40�[0m �[90m[core.py:1354]�[0m Traceback (most recent call last):

[2026-08-21T19:42:40Z] �[0;36m(EngineCore pid=522)�[0;0m �[31mERROR�[0m �[90m08-21 19:42:40�[0m �[90m[core.py:1354]�[0m   File "/usr/local/lib/python3.12/dist-packages/vllm/v1/engine/core.py", line 1316, in run_engine_core

[2026-08-21T19:42:40Z] �[0;36m(EngineCore pid=522)�[0;0m �[31mERROR�[0m �[90m08-21 19:42:40�[0m �[90m[core.py:1354]�[0m     engine_core = EngineCoreProc(*args, engine_index=dp_rank, **kwargs)

[2026-08-21T19:42:40Z] �[0;36m(EngineCore pid=522)�[0;0m �[31mERROR�[0m �[90m08-21 19:42:40�[0m �[90m[core.py:1354]�[0m                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

[2026-08-21T19:42:40Z] �[0;36m(EngineCore pid=522)�[0;0m �[31mERROR�[0m �[90m08-21 19:42:40�[0m �[90m[core.py:1354]�[0m   File "/usr/local/lib/python3.12/dist-packages/vllm/tracing/otel.py", line 178, in sync_wrapper

[2026-08-21T19:42:40Z] �[0;36m(EngineCore pid=522)�[0;0m �[31mERROR�[0m �[90m08-21 19:42:40�[0m �[90m[core.py:1354]�[0m     return func(*args, **kwargs)

[2026-08-21T19:42:40Z] �[0;36m(EngineCore pid=522)�[0;0m �[31mERROR�[0m �[90m08-21 19:42:40�[0m �[90m[core.py:1354]�[0m            ^^^^^^^^^^^^^^^^^^^^^

[2026-08-21T19:42:40Z] �[0;36m(EngineCore pid=522)�[0;0m �[31mERROR�[0m �[90m08-21 19:42:40�[0m �[90m[core.py:1354]�[0m   File "/usr/local/lib/python3.12/dist-packages/vllm/v1/engine/core.py", line 1073, in __init__

[2026-08-21T19:42:40Z] �[0;36m(EngineCore pid=522)�[0;0m �[31mERROR�[0m �[90m08-21 19:42:40�[0m �[90m[core.py:1354]�[0m     super().__init__(

[2026-08-21T19:42:40Z] �[0;36m(EngineCore pid=522)�[0;0m �[31mERROR�[0m �[90m08-21 19:42:40�[0m �[90m[core.py:1354]�[0m   File "/usr/local/lib/python3.12/dist-packages/vllm/v1/engine/core.py", line 133, in __init__

[2026-08-21T19:42:40Z] �[0;36m(EngineCore pid=522)�[0;0m �[31mERROR�[0m �[90m08-21 19:42:40�[0m �[90m[core.py:1354]�[0m     self.model_executor = executor_class(vllm_config)

[2026-08-21T19:42:40Z] �[0;36m(EngineCore pid=522)�[0;0m �[31mERROR�[0m �[90m08-21 19:42:40�[0m �[90m[core.py:1354]�[0m                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^

[2026-08-21T19:42:40Z] �[0;36m(EngineCore pid=522)�[0;0m �[31mERROR�[0m �[90m08-21 19:42:40�[0m �[90m[core.py:1354]�[0m   File "/usr/local/lib/python3.12/dist-packages/vllm/tracing/otel.py", line 178, in sync_wrapper

[2026-08-21T19:42:40Z] �[0;36m(EngineCore pid=522)�[0;0m �[31mERROR�[0m �[90m08-21 19:42:40�[0m �[90m[core.py:1354]�[0m     return func(*args, **kwargs)

[2026-08-21T19:42:40Z] �[0;36m(EngineCore pid=522)�[0;0m �[31mERROR�[0m �[90m08-21 19:42:40�[0m �[90m[core.py:1354]�[0m            ^^^^^^^^^^^^^^^^^^^^^

[2026-08-21T19:42:40Z] �[0;36m(EngineCore pid=522)�[0;0m �[31mERROR�[0m �[90m08-21 19:42:40�[0m �[90m[core.py:1354]�[0m   File "/usr/local/lib/python3.12/dist-packages/vllm/v1/executor/abstract.py", line 110, in __init__

[2026-08-21T19:42:40Z] �[0;36m(EngineCore pid=522)�[0;0m �[31mERROR�[0m �[90m08-21 19:42:40�[0m �[90m[core.py:1354]�[0m     self._init_executor()

[2026-08-21T19:42:40Z] �[0;36m(EngineCore pid=522)�[0;0m �[31mERROR�[0m �[90m08-21 19:42:40�[0m �[90m[core.py:1354]�[0m   File "/usr/local/lib/python3.12/dist-packages/vllm/v1/executor/uniproc_executor.py", line 74, in _init_executor

[2026-08-21T19:42:40Z] �[0;36m(EngineCore pid=522)�[0;0m �[31mERROR�[0m �[90m08-21 19:42:40�[0m �[90m[core.py:1354]�[0m     self.driver_worker.load_model()

[2026-08-21T19:42:40Z] �[0;36m(EngineCore pid=522)�[0;0m �[31mERROR�[0m �[90m08-21 19:42:40�[0m �[90m[core.py:1354]�[0m   File "/usr/local/lib/python3.12/dist-packages/vllm/v1/worker/gpu_worker.py", line 457, in load_model

[2026-08-21T19:42:40Z] �[0;36m(EngineCore pid=522)�[0;0m �[31mERROR�[0m �[90m08-21 19:42:40�[0m �[90m[core.py:1354]�[0m     self.model_runner.load_model(load_dummy_weights=load_dummy_weights)

[2026-08-21T19:42:40Z] �[0;36m(EngineCore pid=522)�[0;0m �[31mERROR�[0m �[90m08-21 19:42:40�[0m �[90m[core.py:1354]�[0m   File "/usr/local/lib/python3.12/dist-packages/vllm/v1/worker/gpu/model_runner.py", line 370, in load_model

[2026-08-21T19:42:40Z] �[0;36m(EngineCore pid=522)�[0;0m �[31mERROR�[0m �[90m08-21 19:42:40�[0m �[90m[core.py:1354]�[0m     self.model = model_loader.load_model(

[2026-08-21T19:42:40Z] �[0;36m(EngineCore pid=522)�[0;0m �[31mERROR�[0m �[90m08-21 19:42:40�[0m �[90m[core.py:1354]�[0m                  ^^^^^^^^^^^^^^^^^^^^^^^^

[2026-08-21T19:42:40Z] �[0;36m(EngineCore pid=522)�[0;0m �[31mERROR�[0m �[90m08-21 19:42:40�[0m �[90m[core.py:1354]�[0m   File "/usr/local/lib/python3.12/dist-packages/vllm/tracing/otel.py", line 178, in sync_wrapper

[2026-08-21T19:42:40Z] �[0;36m(EngineCore pid=522)�[0;0m �[31mERROR�[0m �[90m08-21 19:42:40�[0m �[90m[core.py:1354]�[0m     return func(*args, **kwargs)

[2026-08-21T19:42:40Z] �[0;36m(EngineCore pid=522)�[0;0m �[31mERROR�[0m �[90m08-21 19:42:40�[0m �[90m[core.py:1354]�[0m            ^^^^^^^^^^^^^^^^^^^^^

[2026-08-21T19:42:40Z] �[0;36m(EngineCore pid=522)�[0;0m �[31mERROR�[0m �[90m08-21 19:42:40�[0m �[90m[core.py:1354]�[0m   File "/usr/local/lib/python3.12/dist-packages/vllm/model_executor/model_loader/base_loader.py", line 55, in load_model

[2026-08-21T19:42:40Z] �[0;36m(EngineCore pid=522)�[0;0m �[31mERROR�[0m �[90m08-21 19:42:40�[0m �[90m[core.py:1354]�[0m     model = initialize_model(

[2026-08-21T19:42:40Z] �[0;36m(EngineCore pid=522)�[0;0m �[31mERROR�[0m �[90m08-21 19:42:40�[0m �[90m[core.py:1354]�[0m             ^^^^^^^^^^^^^^^^^

[2026-08-21T19:42:40Z] �[0;36m(EngineCore pid=522)�[0;0m �[31mERROR�[0m �[90m08-21 19:42:40�[0m �[90m[core.py:1354]�[0m   File "/usr/local/lib/python3.12/dist-packages/vllm/tracing/otel.py", line 178, in sync_wrapper

[2026-08-21T19:42:40Z] �[0;36m(EngineCore pid=522)�[0;0m �[31mERROR�[0m �[90m08-21 19:42:40�[0m �[90m[core.py:1354]�[0m     return func(*args, **kwargs)

[2026-08-21T19:42:40Z] �[0;36m(EngineCore pid=522)�[0;0m �[31mERROR�[0m �[90m08-21 19:42:40�[0m �[90m[core.py:1354]�[0m            ^^^^^^^^^^^^^^^^^^^^^

[2026-08-21T19:42:40Z] �[0;36m(EngineCore pid=522)�[0;0m �[31mERROR�[0m �[90m08-21 19:42:40�[0m �[90m[core.py:1354]�[0m   File "/usr/local/lib/python3.12/dist-packages/vllm/model_executor/model_loader/utils.py", line 59, in initialize_model

[2026-08-21T19:42:40Z] �[0;36m(EngineCore pid=522)�[0;0m �[31mERROR�[0m �[90m08-21 19:42:40�[0m �[90m[core.py:1354]�[0m     model = model_class(vllm_config=vllm_config, prefix=prefix)

[2026-08-21T19:42:40Z] �[0;36m(EngineCore pid=522)�[0;0m �[31mERROR�[0m �[90m08-21 19:42:40�[0m �[90m[core.py:1354]�[0m             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

[2026-08-21T19:42:40Z] �[0;36m(EngineCore pid=522)�[0;0m �[31mERROR�[0m �[90m08-21 19:42:40�[0m �[90m[core.py:1354]�[0m   File "/usr/local/lib/python3.12/dist-packages/vllm/model_executor/models/deepseek_v2.py", line 1874, in __init__

[2026-08-21T19:42:40Z] �[0;36m(EngineCore pid=522)�[0;0m �[31mERROR�[0m �[90m08-21 19:42:40�[0m �[90m[core.py:1354]�[0m     self.model = self.model_cls(

[2026-08-21T19:42:40Z] �[0;36m(EngineCore pid=522)�[0;0m �[31mERROR�[0m �[90m08-21 19:42:40�[0m �[90m[core.py:1354]�[0m                  ^^^^^^^^^^^^^^^

[2026-08-21T19:42:40Z] �[0;36m(EngineCore pid=522)�[0;0m �[31mERROR�[0m �[90m08-21 19:42:40�[0m �[90m[core.py:1354]�[0m   File "/usr/local/lib/python3.12/dist-packages/vllm/compilation/decorators.py", line 383, in __init__

[2026-08-21T19:42:40Z] �[0;36m(EngineCore pid=522)�[0;0m �[31mERROR�[0m �[90m08-21 19:42:40�[0m �[90m[core.py:1354]�[0m     old_init(self, *args, **kwargs)

[2026-08-21T19:42:40Z] �[0;36m(EngineCore pid=522)�[0;0m �[31mERROR�[0m �[90m08-21 19:42:40�[0m �[90m[core.py:1354]�[0m   File "/usr/local/lib/python3.12/dist-packages/vllm/model_executor/models/deepseek_v2.py", line 1429, in __init__

[2026-08-21T19:42:40Z] �[0;36m(EngineCore pid=522)�[0;0m �[31mERROR�[0m �[90m08-21 19:42:40�[0m �[90m[core.py:1354]�[0m     self.start_layer, self.end_layer, self.layers = make_layers(

[2026-08-21T19:42:40Z] �[0;36m(EngineCore pid=522)�[0;0m �[31mERROR�[0m �[90m08-21 19:42:40�[0m �[90m[core.py:1354]�[0m                                                     ^^^^^^^^^^^^

[2026-08-21T19:42:40Z] �[0;36m(EngineCore pid=522)�[0;0m �[31mERROR�[0m �[90m08-21 19:42:40�[0m �[90m[core.py:1354]�[0m   File "/usr/local/lib/python3.12/dist-packages/vllm/model_executor/models/utils.py", line 858, in make_layers

[2026-08-21T19:42:40Z] �[0;36m(EngineCore pid=522)�[0;0m �[31mERROR�[0m �[90m08-21 19:42:40�[0m �[90m[core.py:1354]�[0m     + get_offloader().wrap_modules(

[2026-08-21T19:42:40Z] �[0;36m(EngineCore pid=522)�[0;0m �[31mERROR�[0m �[90m08-21 19:42:40�[0m �[90m[core.py:1354]�[0m       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

[2026-08-21T19:42:40Z] �[0;36m(EngineCore pid=522)�[0;0m �[31mERROR�[0m �[90m08-21 19:42:40�[0m �[90m[core.py:1354]�[0m   File "/usr/local/lib/python3.12/dist-packages/vllm/model_executor/offloader/base.py", line 104, in wrap_modules

[2026-08-21T19:42:40Z] �[0;36m(EngineCore pid=522)�[0;0m �[31mERROR�[0m �[90m08-21 19:42:40�[0m �[90m[core.py:1354]�[0m     return list(modules_generator)

[2026-08-21T19:42:40Z] �[0;36m(EngineCore pid=522)�[0;0m �[31mERROR�[0m �[90m08-21 19:42:40�[0m �[90m[core.py:1354]�[0m            ^^^^^^^^^^^^^^^^^^^^^^^

[2026-08-21T19:42:40Z] �[0;36m(EngineCore pid=522)�[0;0m �[31mERROR�[0m �[90m08-21 19:42:40�[0m �[90m[core.py:1354]�[0m   File "/usr/local/lib/python3.12/dist-packages/vllm/model_executor/models/utils.py", line 859, in <genexpr>

[2026-08-21T19:42:40Z] �[0;36m(EngineCore pid=522)�[0;0m �[31mERROR�[0m �[90m08-21 19:42:40�[0m �[90m[core.py:1354]�[0m     layer_fn(prefix=f"{prefix}.{idx}") for idx in range(start_layer, end_layer)

[2026-08-21T19:42:40Z] �[0;36m(EngineCore pid=522)�[0;0m �[31mERROR�[0m �[90m08-21 19:42:40�[0m �[90m[core.py:1354]�[0m     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

[2026-08-21T19:42:40Z] �[0;36m(EngineCore pid=522)�[0;0m �[31mERROR�[0m �[90m08-21 19:42:40�[0m �[90m[core.py:1354]�[0m   File "/usr/local/lib/python3.12/dist-packages/vllm/model_executor/models/deepseek_v2.py", line 1431, in <lambda>

[2026-08-21T19:42:40Z] �[0;36m(EngineCore pid=522)�[0;0m �[31mERROR�[0m �[90m08-21 19:42:40�[0m �[90m[core.py:1354]�[0m     lambda prefix: DeepseekV2DecoderLayer(

[2026-08-21T19:42:40Z] �[0;36m(EngineCore pid=522)�[0;0m �[31mERROR�[0m �[90m08-21 19:42:40�[0m �[90m[core.py:1354]�[0m                    ^^^^^^^^^^^^^^^^^^^^^^^

[2026-08-21T19:42:40Z] �[0;36m(EngineCore pid=522)�[0;0m �[31mERROR�[0m �[90m08-21 19:42:40�[0m �[90m[core.py:1354]�[0m   File "/usr/local/lib/python3.12/dist-packages/vllm/model_executor/models/deepseek_v2.py", line 1299, in __init__

[2026-08-21T19:42:40Z] �[0;36m(EngineCore pid=522)�[0;0m �[31mERROR�[0m �[90m08-21 19:42:40�[0m �[90m[core.py:1354]�[0m     self.mlp = DeepseekV2MoE(

[2026-08-21T19:42:40Z] �[0;36m(EngineCore pid=522)�[0;0m �[31mERROR�[0m �[90m08-21 19:42:40�[0m �[90m[core.py:1354]�[0m                ^^^^^^^^^^^^^^

[2026-08-21T19:42:40Z] �[0;36m(EngineCore pid=522)�[0;0m �[31mERROR�[0m �[90m08-21 19:42:40�[0m �[90m[core.py:1354]�[0m   File "/usr/local/lib/python3.12/dist-packages/vllm/model_executor/models/deepseek_v2.py", line 370, in __init__

[2026-08-21T19:42:40Z] �[0;36m(EngineCore pid=522)�[0;0m �[31mERROR�[0m �[90m08-21 19:42:40�[0m �[90m[core.py:1354]�[0m     self.experts = FusedMoEFactory(

[2026-08-21T19:42:40Z] �[0;36m(EngineCore pid=522)�[0;0m �[31mERROR�[0m �[90m08-21 19:42:40�[0m �[90m[core.py:1354]�[0m                    ^^^^^^^^^^^^^^^^

[2026-08-21T19:42:40Z] �[0;36m(EngineCore pid=522)�[0;0m �[31mERROR�[0m �[90m08-21 19:42:40�[0m �[90m[core.py:1354]�[0m   File "/usr/local/lib/python3.12/dist-packages/vllm/model_executor/layers/fused_moe/layer.py", line 366, in FusedMoEFactory

[2026-08-21T19:42:40Z] �[0;36m(EngineCore pid=522)�[0;0m �[31mERROR�[0m �[90m08-21 19:42:40�[0m �[90m[core.py:1354]�[0m     routed_experts = routed_experts_cls(

[2026-08-21T19:42:40Z] �[0;36m(EngineCore pid=522)�[0;0m �[31mERROR�[0m �[90m08-21 19:42:40�[0m �[90m[core.py:1354]�[0m                      ^^^^^^^^^^^^^^^^^^^

[2026-08-21T19:42:40Z] �[0;36m(EngineCore pid=522)�[0;0m �[31mERROR�[0m �[90m08-21 19:42:40�[0m �[90m[core.py:1354]�[0m   File "/usr/local/lib/python3.12/dist-packages/vllm/model_executor/layers/fused_moe/routed_experts.py", line 176, in __init__

[2026-08-21T19:42:40Z] �[0;36m(EngineCore pid=522)�[0;0m �[31mERROR�[0m �[90m08-21 19:42:40�[0m �[90m[core.py:1354]�[0m     self.quant_method.create_weights(layer=self, **moe_quant_params)

[2026-08-21T19:42:40Z] �[0;36m(EngineCore pid=522)�[0;0m �[31mERROR�[0m �[90m08-21 19:42:40�[0m �[90m[core.py:1354]�[0m   File "/usr/local/lib/python3.12/dist-packages/vllm/model_executor/layers/fused_moe/unquantized_fused_moe_method.py", line 70, in create_weights

[2026-08-21T19:42:40Z] �[0;36m(EngineCore pid=522)�[0;0m �[31mERROR�[0m �[90m08-21 19:42:40�[0m �[90m[core.py:1354]�[0m     torch.empty(

[2026-08-21T19:42:40Z] �[0;36m(EngineCore pid=522)�[0;0m �[31mERROR�[0m �[90m08-21 19:42:40�[0m �[90m[core.py:1354]�[0m   File "/usr/local/lib/python3.12/dist-packages/torch/utils/_device.py", line 122, in __torch_function__

[2026-08-21T19:42:40Z] �[0;36m(EngineCore pid=522)�[0;0m �[31mERROR�[0m �[90m08-21 19:42:40�[0m �[90m[core.py:1354]�[0m     return func(*args, **kwargs)

[2026-08-21T19:42:40Z] �[0;36m(EngineCore pid=522)�[0;0m �[31mERROR�[0m �[90m08-21 19:42:40�[0m �[90m[core.py:1354]�[0m            ^^^^^^^^^^^^^^^^^^^^^

[2026-08-21T19:42:40Z] �[0;36m(EngineCore pid=522)�[0;0m �[31mERROR�[0m �[90m08-21 19:42:40�[0m �[90m[core.py:1354]�[0m torch.OutOfMemoryError: CUDA out of memory. Tried to allocate 704.00 MiB. GPU 0 has a total capacity of 22.05 GiB of which 426.12 MiB is free. Including non-PyTorch memory, this process has 21.62 GiB memory in use. Of the allocated memory 21.36 GiB is allocated by PyTorch, and 33.40 MiB is reserved by PyTorch but unallocated. If reserved but unallocated memory is large try setting PYTORCH_CUDA_ALLOC_CONF=expandable_segments:True to avoid fragmentation.  See documentation for Memory Management  (https://docs.pytorch.org/docs/stable/notes/cuda.html#optimizing-memory-usage-with-pytorch-cuda-alloc-conf)
[2026-08-21T19:42:40Z] �[0;36m(EngineCore pid=522)�[0;0m Process EngineCore:
[2026-08-21T19:42:40Z] �[0;36m(EngineCore pid=522)�[0;0m Traceback (most recent call last):
[2026-08-21T19:42:40Z] �[0;36m(EngineCore pid=522)�[0;0m   File "/usr/lib/python3.12/multiprocessing/process.py", line 314, in _bootstrap
[2026-08-21T19:42:40Z] �[0;36m(EngineCore pid=522)�[0;0m     self.run()
[2026-08-21T19:42:40Z] �[0;36m(EngineCore pid=522)�[0;0m   File "/usr/lib/python3.12/multiprocessing/process.py", line 108, in run
[2026-08-21T19:42:40Z] �[0;36m(EngineCore pid=522)�[0;0m     self._target(*self._args, **self._kwargs)
[2026-08-21T19:42:40Z] �[0;36m(EngineCore pid=522)�[0;0m   File "/usr/local/lib/python3.12/dist-packages/vllm/v1/engine/core.py", line 1358, in run_engine_core
[2026-08-21T19:42:40Z] �[0;36m(EngineCore pid=522)�[0;0m     raise e
[2026-08-21T19:42:40Z] �[0;36m(EngineCore pid=522)�[0;0m   File "/usr/local/lib/python3.12/dist-packages/vllm/v1/engine/core.py", line 1316, in run_engine_core
[2026-08-21T19:42:40Z] �[0;36m(EngineCore pid=522)�[0;0m     engine_core = EngineCoreProc(*args, engine_index=dp_rank, **kwargs)
[2026-08-21T19:42:40Z] �[0;36m(EngineCore pid=522)�[0;0m                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
[2026-08-21T19:42:40Z] �[0;36m(EngineCore pid=522)�[0;0m   File "/usr/local/lib/python3.12/dist-packages/vllm/tracing/otel.py", line 178, in sync_wrapper
[2026-08-21T19:42:40Z] �[0;36m(EngineCore pid=522)�[0;0m     return func(*args, **kwargs)
[2026-08-21T19:42:40Z] �[0;36m(EngineCore pid=522)�[0;0m            ^^^^^^^^^^^^^^^^^^^^^
[2026-08-21T19:42:40Z] �[0;36m(EngineCore pid=522)�[0;0m   File "/usr/local/lib/python3.12/dist-packages/vllm/v1/engine/core.py", line 1073, in __init__
[2026-08-21T19:42:40Z] �[0;36m(EngineCore pid=522)�[0;0m     super().__init__(
[2026-08-21T19:42:40Z] �[0;36m(EngineCore pid=522)�[0;0m   File "/usr/local/lib/python3.12/dist-packages/vllm/v1/engine/core.py", line 133, in __init__
[2026-08-21T19:42:40Z] �[0;36m(EngineCore pid=522)�[0;0m     self.model_executor = executor_class(vllm_config)
[2026-08-21T19:42:40Z] �[0;36m(EngineCore pid=522)�[0;0m                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^
[2026-08-21T19:42:40Z] �[0;36m(EngineCore pid=522)�[0;0m   File "/usr/local/lib/python3.12/dist-packages/vllm/tracing/otel.py", line 178, in sync_wrapper
[2026-08-21T19:42:40Z] �[0;36m(EngineCore pid=522)�[0;0m     return func(*args, **kwargs)
[2026-08-21T19:42:40Z] �[0;36m(EngineCore pid=522)�[0;0m            ^^^^^^^^^^^^^^^^^^^^^
[2026-08-21T19:42:40Z] �[0;36m(EngineCore pid=522)�[0;0m   File "/usr/local/lib/python3.12/dist-packages/vllm/v1/executor/abstract.py", line 110, in __init__
[2026-08-21T19:42:40Z] �[0;36m(EngineCore pid=522)�[0;0m     self._init_executor()
[2026-08-21T19:42:40Z] �[0;36m(EngineCore pid=522)�[0;0m   File "/usr/local/lib/python3.12/dist-packages/vllm/v1/executor/uniproc_executor.py", line 74, in _init_executor
[2026-08-21T19:42:40Z] �[0;36m(EngineCore pid=522)�[0;0m     self.driver_worker.load_model()
[2026-08-21T19:42:40Z] �[0;36m(EngineCore pid=522)�[0;0m   File "/usr/local/lib/python3.12/dist-packages/vllm/v1/worker/gpu_worker.py", line 457, in load_model
[2026-08-21T19:42:40Z] �[0;36m(EngineCore pid=522)�[0;0m     self.model_runner.load_model(load_dummy_weights=load_dummy_weights)
[2026-08-21T19:42:40Z] �[0;36m(EngineCore pid=522)�[0;0m   File "/usr/local/lib/python3.12/dist-packages/vllm/v1/worker/gpu/model_runner.py", line 370, in load_model
[2026-08-21T19:42:40Z] �[0;36m(EngineCore pid=522)�[0;0m     self.model = model_loader.load_model(
[2026-08-21T19:42:40Z] �[0;36m(EngineCore pid=522)�[0;0m                  ^^^^^^^^^^^^^^^^^^^^^^^^
[2026-08-21T19:42:40Z] �[0;36m(EngineCore pid=522)�[0;0m   File "/usr/local/lib/python3.12/dist-packages/vllm/tracing/otel.py", line 178, in sync_wrapper
[2026-08-21T19:42:40Z] �[0;36m(EngineCore pid=522)�[0;0m     return func(*args, **kwargs)
[2026-08-21T19:42:40Z] �[0;36m(EngineCore pid=522)�[0;0m            ^^^^^^^^^^^^^^^^^^^^^
[2026-08-21T19:42:40Z] �[0;36m(EngineCore pid=522)�[0;0m   File "/usr/local/lib/python3.12/dist-packages/vllm/model_executor/model_loader/base_loader.py", line 55, in load_model
[2026-08-21T19:42:40Z] �[0;36m(EngineCore pid=522)�[0;0m     model = initialize_model(
[2026-08-21T19:42:40Z] �[0;36m(EngineCore pid=522)�[0;0m             ^^^^^^^^^^^^^^^^^
[2026-08-21T19:42:40Z] �[0;36m(EngineCore pid=522)�[0;0m   File "/usr/local/lib/python3.12/dist-packages/vllm/tracing/otel.py", line 178, in sync_wrapper
[2026-08-21T19:42:40Z] �[0;36m(EngineCore pid=522)�[0;0m     return func(*args, **kwargs)
[2026-08-21T19:42:40Z] �[0;36m(EngineCore pid=522)�[0;0m            ^^^^^^^^^^^^^^^^^^^^^
[2026-08-21T19:42:40Z] �[0;36m(EngineCore pid=522)�[0;0m   File "/usr/local/lib/python3.12/dist-packages/vllm/model_executor/model_loader/utils.py", line 59, in initialize_model
[2026-08-21T19:42:40Z] �[0;36m(EngineCore pid=522)�[0;0m     model = model_class(vllm_config=vllm_config, prefix=prefix)
[2026-08-21T19:42:40Z] �[0;36m(EngineCore pid=522)�[0;0m             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
[2026-08-21T19:42:40Z] �[0;36m(EngineCore pid=522)�[0;0m   File "/usr/local/lib/python3.12/dist-packages/vllm/model_executor/models/deepseek_v2.py", line 1874, in __init__
[2026-08-21T19:42:40Z] �[0;36m(EngineCore pid=522)�[0;0m     self.model = self.model_cls(
[2026-08-21T19:42:40Z] �[0;36m(EngineCore pid=522)�[0;0m                  ^^^^^^^^^^^^^^^
[2026-08-21T19:42:40Z] �[0;36m(EngineCore pid=522)�[0;0m   File "/usr/local/lib/python3.12/dist-packages/vllm/compilation/decorators.py", line 383, in __init__
[2026-08-21T19:42:40Z] �[0;36m(EngineCore pid=522)�[0;0m     old_init(self, *args, **kwargs)
[2026-08-21T19:42:40Z] �[0;36m(EngineCore pid=522)�[0;0m   File "/usr/local/lib/python3.12/dist-packages/vllm/model_executor/models/deepseek_v2.py", line 1429, in __init__
[2026-08-21T19:42:40Z] �[0;36m(EngineCore pid=522)�[0;0m     self.start_layer, self.end_layer, self.layers = make_layers(
[2026-08-21T19:42:40Z] �[0;36m(EngineCore pid=522)�[0;0m                                                     ^^^^^^^^^^^^
[2026-08-21T19:42:40Z] �[0;36m(EngineCore pid=522)�[0;0m   File "/usr/local/lib/python3.12/dist-packages/vllm/model_executor/models/utils.py", line 858, in make_layers
[2026-08-21T19:42:40Z] �[0;36m(EngineCore pid=522)�[0;0m     + get_offloader().wrap_modules(
[2026-08-21T19:42:40Z] �[0;36m(EngineCore pid=522)�[0;0m       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
[2026-08-21T19:42:40Z] �[0;36m(EngineCore pid=522)�[0;0m   File "/usr/local/lib/python3.12/dist-packages/vllm/model_executor/offloader/base.py", line 104, in wrap_modules
[2026-08-21T19:42:40Z] �[0;36m(EngineCore pid=522)�[0;0m     return list(modules_generator)
[2026-08-21T19:42:40Z] �[0;36m(EngineCore pid=522)�[0;0m            ^^^^^^^^^^^^^^^^^^^^^^^
[2026-08-21T19:42:40Z] �[0;36m(EngineCore pid=522)�[0;0m   File "/usr/local/lib/python3.12/dist-packages/vllm/model_executor/models/utils.py", line 859, in <genexpr>
[2026-08-21T19:42:40Z] �[0;36m(EngineCore pid=522)�[0;0m     layer_fn(prefix=f"{prefix}.{idx}") for idx in range(start_layer, end_layer)
[2026-08-21T19:42:40Z] �[0;36m(EngineCore pid=522)�[0;0m     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
[2026-08-21T19:42:40Z] �[0;36m(EngineCore pid=522)�[0;0m   File "/usr/local/lib/python3.12/dist-packages/vllm/model_executor/models/deepseek_v2.py", line 1431, in <lambda>
[2026-08-21T19:42:40Z] �[0;36m(EngineCore pid=522)�[0;0m     lambda prefix: DeepseekV2DecoderLayer(
[2026-08-21T19:42:40Z] �[0;36m(EngineCore pid=522)�[0;0m                    ^^^^^^^^^^^^^^^^^^^^^^^
[2026-08-21T19:42:40Z] �[0;36m(EngineCore pid=522)�[0;0m   File "/usr/local/lib/python3.12/dist-packages/vllm/model_executor/models/deepseek_v2.py", line 1299, in __init__
[2026-08-21T19:42:40Z] �[0;36m(EngineCore pid=522)�[0;0m     self.mlp = DeepseekV2MoE(
[2026-08-21T19:42:40Z] �[0;36m(EngineCore pid=522)�[0;0m                ^^^^^^^^^^^^^^
[2026-08-21T19:42:40Z] �[0;36m(EngineCore pid=522)�[0;0m   File "/usr/local/lib/python3.12/dist-packages/vllm/model_executor/models/deepseek_v2.py", line 370, in __init__
[2026-08-21T19:42:40Z] �[0;36m(EngineCore pid=522)�[0;0m     self.experts = FusedMoEFactory(
[2026-08-21T19:42:40Z] �[0;36m(EngineCore pid=522)�[0;0m                    ^^^^^^^^^^^^^^^^
[2026-08-21T19:42:40Z] �[0;36m(EngineCore pid=522)�[0;0m   File "/usr/local/lib/python3.12/dist-packages/vllm/model_executor/layers/fused_moe/layer.py", line 366, in FusedMoEFactory
[2026-08-21T19:42:40Z] �[0;36m(EngineCore pid=522)�[0;0m     routed_experts = routed_experts_cls(
[2026-08-21T19:42:40Z] �[0;36m(EngineCore pid=522)�[0;0m                      ^^^^^^^^^^^^^^^^^^^
[2026-08-21T19:42:40Z] �[0;36m(EngineCore pid=522)�[0;0m   File "/usr/local/lib/python3.12/dist-packages/vllm/model_executor/layers/fused_moe/routed_experts.py", line 176, in __init__
[2026-08-21T19:42:40Z] �[0;36m(EngineCore pid=522)�[0;0m     self.quant_method.create_weights(layer=self, **moe_quant_params)
[2026-08-21T19:42:40Z] �[0;36m(EngineCore pid=522)�[0;0m   File "/usr/local/lib/python3.12/dist-packages/vllm/model_executor/layers/fused_moe/unquantized_fused_moe_method.py", line 70, in create_weights
[2026-08-21T19:42:40Z] �[0;36m(EngineCore pid=522)�[0;0m     torch.empty(
[2026-08-21T19:42:40Z] �[0;36m(EngineCore pid=522)�[0;0m   File "/usr/local/lib/python3.12/dist-packages/torch/utils/_device.py", line 122, in __torch_function__
[2026-08-21T19:42:40Z] �[0;36m(EngineCore pid=522)�[0;0m     return func(*args, **kwargs)
[2026-08-21T19:42:40Z] �[0;36m(EngineCore pid=522)�[0;0m            ^^^^^^^^^^^^^^^^^^^^^
[2026-08-21T19:42:40Z] �[0;36m(EngineCore pid=522)�[0;0m torch.OutOfMemoryError: CUDA out of memory. Tried to allocate 704.00 MiB. GPU 0 has a total capacity of 22.05 GiB of which 426.12 MiB is free. Including non-PyTorch memory, this process has 21.62 GiB memory in use. Of the allocated memory 21.36 GiB is allocated by PyTorch, and 33.40 MiB is reserved by PyTorch but unallocated. If reserved but unallocated memory is large try setting PYTORCH_CUDA_ALLOC_CONF=expandable_segments:True to avoid fragmentation.  See documentation for Memory Management  (https://docs.pytorch.org/docs/stable/notes/cuda.html#optimizing-memory-usage-with-pytorch-cuda-alloc-conf)
[2026-08-21T19:42:40Z] �[32mINFO�[0m �[90m08-21 19:42:40�[0m �[90m[utils.py:615]�[0m [shutdown] Process manager: send sigterm to process EngineCore
[2026-08-21T19:42:41Z] �[31mFAILED�[0m

@SumanthRH

Copy link
Copy Markdown
Contributor

Only the unit/ integration tests have run. The E2E examples are yet to run in CI.

@SumanthRH

Copy link
Copy Markdown
Contributor

Overall the changes here LGTM as a first step for a RDT engine pending CI fixes

Signed-off-by: haoaaron <ahao@anyscale.com>
@hao-aaron

Copy link
Copy Markdown
Contributor Author

/ci run

@github-actions

Copy link
Copy Markdown

✅ Triggered Buildkite CI #85120 for commit 5ec2ef70183f.

@AndreasKaratzas

Copy link
Copy Markdown
Member

/ci run

@github-actions

Copy link
Copy Markdown

✅ Triggered Buildkite CI #85128 for commit 0e61f03d63a8.

hao-aaron and others added 2 commits August 21, 2026 22:55
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: haoaaron <ahao@anyscale.com>
@mergify mergify Bot added the cpu Related to CPU backends label Aug 22, 2026
@hao-aaron

Copy link
Copy Markdown
Contributor Author

/ci run

@github-actions

Copy link
Copy Markdown

✅ Triggered Buildkite CI #85157 for commit b414dd1acc83.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ci/build cpu Related to CPU backends documentation Improvements or additions to documentation kimi nvidia qwen Related to Qwen models ray anything related with ray ready ONLY add when PR is ready to merge/full CI is needed v1

Projects

Status: No status
Status: Backlog

Development

Successfully merging this pull request may close these issues.

4 participants