feat(refit): add a ModelExpress-brokered nccl_reshard transport - #3720
Open
yixinh-nv wants to merge 11 commits into
Open
feat(refit): add a ModelExpress-brokered nccl_reshard transport#3720yixinh-nv wants to merge 11 commits into
yixinh-nv wants to merge 11 commits into
Conversation
Adds refit_transport=mx_nccl_reshard, which keeps the existing nccl_reshard wire path and replaces only the communicator bootstrap. The bulk path still ends in nccl.m2n.reshard with the same meshes and placements, and the misc path still rides the packed broadcast, so the two transports move identical bytes and can be compared directly. What changes is the rendezvous. StatelessProcessGroup is a TCPStore whose only job is to move 128 bytes of ncclUniqueId from rank 0 to everyone else, at an address the driver allocates per PP stage. Brokering that through ModelExpress adds admission against an expected participant set, fencing of worker generations, and a readiness state the trainer can observe before entering the collective -- so a missing worker becomes a bounded failure naming the slot rather than a hang. It also drops the per-stage port allocation from the driver. mx_collective_plan.py is the only genuinely new logic: it expresses the metadata build_nccl_reshard_refit_info already produces in MX's plan vocabulary. It re-derives nothing. A mesh that is not contiguous and ascending cannot be expressed as MX's (shape, rank_offset) and is rejected rather than approximated, because a wrong mesh does not fail the collective, it moves the wrong bytes. Signed-off-by: Yixin Huang <yixinh@nvidia.com>
This was referenced Aug 19, 2026
yixinh-nv
marked this pull request as ready for review
August 19, 2026 23:04
Restructures the integration around what xferdtensor actually consumes from a process group: nccl_communicator, plus broadcast for the packed misc path. Producing an object with that surface whose ncclUniqueId came from ModelExpress -- and dropping it where StatelessProcessGroup normally goes -- means nccl_reshard_refit runs unchanged. That replaces the parallel refit loop the first draft added. It is less code, and it is the difference between the claim 'only the bootstrap changes' being approximately true and being structurally true: below the bootstrap the two transports are now the same code path, so they cannot drift and a measured comparison between them means something. Everything here was found by running it on 8xH200: - mx_collective_bootstrap.py: MxProcessGroup and the join/publish/await sequence. Lanes are created in ascending order because a generator joins every reshard lane while each lane's trainers are blocked inside their own bootstrap, so the two sides have to unblock in the same sequence. - worker + actor-group methods on both sides, replacing the four that the first draft assumed existed. - config.py and grpo.py accept the transport rather than routing it into the checkpoint-engine path. - mx_server_url is now required instead of defaulting. Signed-off-by: Yixin Huang <yixinh@nvidia.com>
Creating two NCCL communicators concurrently across overlapping rank sets deadlocks. The nccl_reshard path already knows this -- it says so where it separates model_update_group from the per-stage groups, and it enforces the separation with a ray.get barrier between the two. build_mx_groups did not. Each worker created every lane it belonged to in a single loop, so a rank that finished one lane started the next while other ranks were still inside the previous one. With a 2-stage pipeline that is a 6-rank reshard lane under construction at the same time as the 8-rank broadcast lane, sharing ranks: the collective never completes and there is no error, only silence. Bootstrap is now driven in phases from the synchronizer -- rendezvous, then one lane at a time in a fixed cluster-wide order, then finish -- with a barrier between each. A worker that does not belong to a lane returns immediately and waits at the barrier rather than racing ahead into its own next lane. Broadcast goes first because every rank is in it, which makes the barrier after it a full-cluster sync point. No change to what is built, only to when: the same lanes, the same ranks, the same identifiers. build_mx_groups survives as a single-process wrapper for tests, where nothing else is creating communicators concurrently. Signed-off-by: Yixin Huang <yixinh@nvidia.com>
ncclGetUniqueId opens the bootstrap listening socket as a side effect, so the identifier is not inert data -- it names a socket that has to still be accepting when the lane's peers dial in. Minting every lane's identifier at rendezvous meant that socket had to survive an actor-method return, a driver round-trip and a barrier before its lane was built. When it did not, the peers got ECONNREFUSED from an address that looks entirely correct, and NCCL reported it as "remote process exited or there was a network error" -- which reads like a dead peer or a bad fabric, not a stale listener. Each lane now mints and publishes its identifier inside the phase that creates the communicator, so minting and use sit microseconds apart in the same call, the way the TCPStore path has always had it. Waiting is per-lane rather than for whole-group READY, because with mint-at-use the later lanes are not published yet and waiting for READY here would deadlock the ordering it is meant to protect. Client-side only: PublishGroupBootstrap and GetCollectiveGroup already carry everything this needs, so the server and the proto are untouched. Signed-off-by: Yixin Huang <yixinh@nvidia.com>
Signed-off-by: Yixin Huang <yixinh@nvidia.com>
Signed-off-by: Yixin Huang <yixinh@nvidia.com>
Signed-off-by: Yixin Huang <yixinh@nvidia.com>
Signed-off-by: Yixin Huang <yixinh@nvidia.com>
Signed-off-by: Yixin Huang <yixinh@nvidia.com>
Signed-off-by: Yixin Huang <yixinh@nvidia.com>
Signed-off-by: Yixin Huang <yixinh@nvidia.com>
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.
What does this PR do?
Adds
refit_transport=mx_nccl_reshard: the existingnccl_resharddata path, with NCCL communicator bootstrap brokered by ModelExpress instead of a TCPStore.Below communicator creation, the path is unchanged:
nccl.m2n.reshardmoves the sharded bulk tensors and the packed NCCL broadcast moves the miscellaneous tensors. MX coordinates admission, immutable membership, worker-generation fencing, plan agreement, per-lane UID publication, and readiness; it is not the tensor-transfer engine.Design boundary
nccl.m2n.reshardplus packed NCCL broadcast.ncclInProgressas an error.Dependencies
Matched H200 end-to-end results
Both runs used Nebius 8xH200 (two 4-GPU pods), Qwen3-30B-A3B BF16, Megatron TP2/PP2/EP2 to vLLM TP4, the same 240-prompt file (
SHA-256 8fffc35d...be9da1), five GRPO steps, and the same 56.87 GiB refit payload: 54.00 GiB M2N bulk plus 2.87 GiB misc broadcast. The only transport selection wasnccl_reshardversusmx_nccl_reshard.nccl_reshard)mx_nccl_reshard)real nccl.m2n.reshardreal nccl.m2n.reshardFor the MX leg, the control plane admitted 8 workers, reached epoch 1
READY, agreed on the plan digest, and created all three lanes before the first refit.These are enclosing
prepare_for_generation/transfer_and_update_weightstimings: they include Ray/actor dispatch and synchronization in addition to device work. They are not wire-bandwidth measurements. All samples are retained, including the cold first refit and the 0.92 s native outlier.The validation used the pinned #2971 runtime base (
0c595799) plus the validated MX integration hunks and the current PR's bootstrap/cleanup fixes. The run provenance records the exact source hashes, ModelExpress server6f74f08d, and ModelExpress client/data4f85110e.The samples establish that MX preserves the native M2N execution and correctness path. They are too few, and the native leg predates the final Ray-cleanup fix, to claim an MX performance delta. A new native rerun at the final exact heads would make the performance comparison publication-ready.
Key fixes discovered during H200 validation
ncclInProgress.Usage
Every worker on both sides must receive the same ModelExpress server address.
NCCL_COMM_IDmust be unset for this path.Validation status