Gather tensor-parallel sharded parameters on read in a trace - #677
Open
khaiwang wants to merge 1 commit into
Open
Gather tensor-parallel sharded parameters on read in a trace#677khaiwang wants to merge 1 commit into
khaiwang wants to merge 1 commit into
Conversation
Member
|
@khaiwang Hey can you make this off of the 0.8 branch? Theres a TPEnvoy and TPInterleaver so I'd put them there. Theres a similar thing for modeling/tp/ for HF Transformers tensor parallelism |
Under tensor parallelism a parameter read inside a trace returned this rank's slice. A steering cell reading `lm_head.weight[token_id]` on the rank that does not own the token indexed a different token's row (Qwen2.5-0.5B, tp=2: `lm_head.weight.shape[0]` was 75968, half vocab), so the steered output diverged from the single-GPU run without an error. `ParallelEnvoy.__getattr__` (vLLM) and `TPEnvoy.__getattr__` (transformers) now all-gather a tensor attribute read while interleaving, gated on the interleaver's `fragments.enabled`, the same check their ad-hoc `__call__` uses, so a one-rank engine is untouched. vLLM: the sharded dim is the `output_dim` / `input_dim` stamp vLLM puts on each parameter it shards (row-parallel splits the input dim, every other parallel layer the output dim); a tensor without the stamp (a row-parallel bias, a scale) is replicated and passes through. The vocab-parallel head's padding rows are dropped to `org_vocab_size`. transformers: the dim comes from transformers' own `ALL_PARALLEL_STYLES.plan_to_weight_dim` / `plan_to_bias_dim` table and the gather is its `gather_full_tensor`. Fused weights (`qkv_proj`, `gate_up_proj`, `packed_colwise`) come back with rows grouped by rank, the layout their gathered `.output` already has, so `x @ weight.T` and `.output` agree. The gathered tensor is a copy: an in-place edit to it does not reach the model. Outside a trace `layer.weight` is still the slice. Verified on two A100s: tests/vllm/test_tensor_parallel.py 25/25 at tp=2 (vllm 0.19.1), tests/tp 42 passed at tp=2 (transformers 5.15.0). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HEt5ijqEYH9RN64FNzPtpz
khaiwang
force-pushed
the
fix/vllm-tp-param-gather
branch
from
August 25, 2026 17:29
ed981b8 to
59d09d4
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Under tensor parallelism a parameter read inside a trace returned this rank's slice. A steering cell reading
lm_head.weight[token_id]on the rank that does not own the token indexed a different token's row (Qwen2.5-0.5B, tp=2:lm_head.weight.shape[0]was 75968, half the vocab), so the steered output diverged from the single-GPU run without an error.Change
Rebuilt on
0.8inside the TP envoy structures (the earlier version of this PR was ondevand hookedBatcher/ baseEnvoy.__getattr__; none of that remains).ParallelEnvoy.__getattr__(modeling/vllm/envoys.py) andTPEnvoy.__getattr__(modeling/tp/envoys.py) all-gather a tensor attribute read while interleaving, gated on the interleaver'sfragments.enabled, the same check their ad-hoc__call__uses, so a one-rank engine is untouched.output_dim/input_dimstamp vLLM puts on each parameter it shards (row-parallel splits the input dim, every other parallel layer the output dim). A tensor without the stamp (row-parallel bias, a scale) is replicated and passes through. The vocab-parallel head's padding rows are dropped toorg_vocab_size.ALL_PARALLEL_STYLES.plan_to_weight_dim/plan_to_bias_dimtable and the gather is itsgather_full_tensor.Semantics stated in
docs/models/tensor-parallel.md(this replaces the "parameters are not gathered" paragraph):layer.weightin a loop.layer.weight[i] = vdoes not reach the model.qkv_proj,gate_up_proj,packed_colwise) come back with rows grouped by rank, the layout their gathered.outputalready has, sox @ weight.Tand.outputagree.layer.weightis still the slice.Verification (two A100s, tp=2)
tests/vllm/test_tensor_parallel.py(vllm 0.19.1, Qwen2.5-0.5B): 25/25, 19 existing plus newTestShardedParameters(row-parallel weightstorch.equalto the tp=1 reference, fused column weights equal as a row multiset,lm_head.weightfull vocab withweight[vocab-5]equal to the reference row, weight outside a trace still the slice).tests/tp(transformers 5.15.0, tiny-random-Llama): 42 passed, including four new recorded values (gate_proj_weight,down_proj_weight,lm_head_weight, and a lens computed asnorm(hidden) @ lm_head.weight.T), bit-exact across ranks and within drift of the single-GPU run.Note for the tests: saving a raw vLLM
nn.Parameter(envoy.weight.save()) on a tp=1 engine fails atcollect_nnsightpickling (cannot pickle 'weakref.ReferenceType', vLLM'sweight_loaderstamp). Independent of this change; the tests save.weight.data.🤖 Generated with Claude Code
https://claude.ai/code/session_01HEt5ijqEYH9RN64FNzPtpz