Repository navigation
Conversation
|
Documentation preview: https://vllm--58382.org.readthedocs.build/en/58382/ |
…o live parameters
Add WeightTransferConfig.reload_mode ("layerwise" | "direct", default
layerwise) for the nccl and ipc weight-transfer engines. Layerwise reload moves
each layer to meta, loads a temporary copy, re-runs post-load processing and
copies back into the captured storage. For a model whose post-load step leaves
the parameters as the checkpoint has them, that detour is pure overhead.
In direct mode start_reload records the address and layout of every parameter
and buffer, model.load_weights runs unchanged so each weight_loader copy_s into
the live storage, and finish_reload checks that nothing moved; a failure leaves
the model undefined and refuses further updates. There is no rollback and no
abort: a failed reload means the engine must be restarted.
start_reload / finish_reload exist because a second mode has to be dispatched
somewhere, and because finish has to complete whichever mode start began: the
mode is latched on the model rather than re-read from a config that may have
changed in between. Only the two engines that have a mode go through them;
every other caller keeps naming the layerwise functions directly.
Signed-off-by: Ao Shen <aoshen524@gmail.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: aoshen02 <aoshen@inferact.ai>
c898584 to
fbe4d05
Compare
Signed-off-by: aoshen <aoshen@inferact.ai>
…o the live model Replace the earlier direct-reload implementation with the reworked one. At cold start, post-load processing records each tensor's checkpoint layout. begin_direct_reload views every re-laid-out tensor back to that layout on its own storage, the weight loaders write the update in place, and finish_direct_reload clears the one-shot post-load guards and reruns process_weights_after_loading. Addresses stay where CUDA graphs captured them. direct mode refuses draft_lm_head_quantization (speculative config and begin) and sharded RDT transfers. The ipc/nccl engine, weight-transfer config and docs changes of the earlier version are dropped; the PR now touches only the direct reload files. It depends on vllm-project#57733 and vllm-project#62407 (replace_parameter / retire_parameter, kept parameter objects). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: aoshen02 <aoshen@inferact.ai>
begin_direct_reload already refuses draft_lm_head_quantization in the worker that reloads; drop the SpeculativeConfig check. Share the layout span computation between direct.py and _view_in_place. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: aoshen <aoshen524@gmail.com>
…tart - Refuse draft_lm_head_quantization when the layout is recorded (cold start) instead of at the first update. - Record nothing for pre-processed (IPC weight-cache) weights: they are already in runtime layout, so a later begin refuses. - Reuse _extent for the layout span; compute it on a meta tensor before set_, which may grow a too-small CPU storage instead of raising. - Remove unused test scaffolding. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: aoshen <aoshen524@gmail.com>
Multi-part checkpoint weights no longer park parts across load_weights calls. finalize_model_weights keeps only the model-level post-load hook. - Molmo/Molmo2: wte.embedding and wte.new_embedding are written straight into their rows of this rank's embed_tokens shard, in any order or call. - Llama4 DP vision: q/k/v load into their rows of the replicated qkv_proj through the stacked mapping; _consolidate_qkv_weights is gone. - The FP8 multi-part cases (indexer wk dequant for DeepSeek-V2/V3.2, dots3, GLM5-next; GLM5-next FP8 MLA projections; MiMo-V2 fused qkv) and the Jina-v3 LoRA merge go back to main's handling; they move to checkpoint-layout parameters on top of direct reload (vllm-project#58382). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SeFSSrpw7yTpLz2C2sHDKA Signed-off-by: Aaron Hao <aaronhao@inferact.ai>
vllm-project#57733 no longer parks multi-part weights across load_weights calls, so there is nothing to discard at reload start and no parked part to test. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SeFSSrpw7yTpLz2C2sHDKA Signed-off-by: Aaron Hao <aaronhao@inferact.ai>
hao-aaron
left a comment
There was a problem hiding this comment.
Went through this against the RFC tracker. Leaving out anything F2/F4/F5/F7/F8 or PR 5 already covers. A few things I think belong here though:
1. Refuse online quant in this PR. F8 adds support, but until then nothing stops it, and finish_direct_reload pops _already_called_process_weights_after_loading, which is the only thing keeping online FP8 PWAL (online/fp8.py) from re-quantizing already-converted bytes. Same shape as the draft_lm_head_quantization refusal; a test next to that one would be nice.
2. Replaced param objects are silently treated as derived. _checkpoint_views skips anything without _ckpt_layout, so if cold-start PWAL registers a new Parameter under a checkpoint name, begin skips it and the loader writes checkpoint bytes into the runtime layout (a same-numel transpose passes default_weight_loader). F4 snapshots at first begin, after this already happened, so it won't catch it. Could we record the names in record_checkpoint_layout and raise in begin_direct_reload if one now points at an object with no recorded layout? That makes the #62407 contract enforced instead of assumed.
3. Failure state. test_retry_after_aborted_update_matches_cold_start says the worker refuses to run a partly updated model, but I don't see that anywhere. Either add a minimal in-progress/failed flag that begin/finish set, or fix the comment and add it to Known limitations.
Smaller:
- Dispatching direct mode from inside
initialize_layerwise_reload/finalize_layerwise_reloadreads oddly, and the env is checked separately at begin and finish. A smallstart_reload/finish_reloadthat stores the mode on the model would be cleaner (#63599 has one inreload/lifecycle.pyif you want to borrow it). finish_direct_reloadsearches for a"cuda"param; the engines already haveself.device, could just pass it through.- Popping the guard on every module also hits
MoERunnerInterface, which sets it in__init__as a hack. Probably harmless, but limiting it to modules with aquant_methodseems safer. _view_in_placebounds writes bystorage.nbytes()rather than the tensor's own range, so it assumes nothing else lives in that storage. Fine if F4/F7 add an overlap check, just want to make sure it's tracked.- Add
VLLM_RELOAD_MODEto the ignored set incompile_factorsso toggling it doesn't invalidate the compile cache.
This review/comment was written by an AI agent (Claude Code) on my behalf.
|
This pull request has merge conflicts that must be resolved before it can be |
Problem
RL weight sync reloads the checkpoint into a serving engine. The layerwise reload moves every layer to meta, materializes it again, loads the checkpoint and reruns post-load processing per layer. That costs a second copy of each layer at a time, and its correctness depends on per-layer completeness bookkeeping, so partial or reordered updates are hard to get right.
Fix
VLLM_RELOAD_MODE=direct(registered invllm/envs.py; the default stayslayerwise) writes checkpoint tensors straight into the live model and reruns cold-start post-load processing once, at the startup addresses:process_weights_after_loading(PWAL), record each tensor's checkpoint layout.begin_direct_reload, called frominitialize_layerwise_reload, views every tensor whose layout PWAL changed back to its checkpoint layout, on the same storage. The loaders then write the checkpoint tensors in place.finish_direct_reload, called fromfinalize_layerwise_reload, clears the one-shot "already processed" guards and reruns PWAL for every module, then the model-level hook, as cold start does.replace_parameterkeeps a parameter's address when PWAL changes its layout: under direct reload, the new tensor is written into a view of the existing storage when it fits.draft_lm_head_quantization. The quantized draft lm_head would be written into the target lm_head it shares.begin, before anything is modified:sharded_rdtrefuses direct mode at init, because its replay plan is baked by a layerwise (meta) dry run.Delivery contract. An update must deliver every checkpoint tensor of each module whose PWAL changes the tensor's layout or is not idempotent. Tensors without such processing, for example a frozen embedding table, may be left out and keep their bytes. Nothing in this PR checks the contract: until the completeness follow-up merges, a violation is not detected.
PWAL must keep every checkpoint tensor registered under its checkpoint name. This builds on #62407 (retire/alias instead of delete/rename).
Known limitations. Non-default MoE backends whose PWAL reallocates derived constants on a rerun are not yet refilled in place: TRTLLM FP8 per-tensor, FlashInfer CUTLASS NVFP4/MXFP4, CuteDSL, and compressed-tensors FP8 constants. They are fixed in the derived-tensor follow-up. Online quantization is supported by a later follow-up.
Tests
tests/model_executor/model_loader/test_direct_reload.pyuses a toy model with the hazards found on real models: a parent-derived tensor, a layout change, a loader bypass, a consumed scale and non-idempotent packing. It checks:Dependencies / merge order
#57733 → #62407 → this PR. The follow-ups stack on this one: completeness check → moved-address/host-float check → side-tensor detector. The derived-tensor in-place PR, the NCCL
empty_cachefix and the loader-bypass fix are independent.🤖 Generated with Claude Code