dynamo: keep native NCCL collectives out of Myelin ForeignNode fusion (cast-boundary)#4388
Conversation
The native DistCollective converters hardcoded the world group (groups = np.arange(world_size), num_ranks = world_size), so a collective could only ever run over all ranks. This breaks 2-D device meshes where a collective runs over a subgroup -- e.g. Ulysses/context-parallel all_to_all over a CP subgroup while tensor-parallel all_reduce uses a separate TP subgroup. TensorRT then rejects it, e.g.: "All to All requires first input dimension to be divisible by nbRanks" because nbRanks was the world size (8) rather than the subgroup size (4). Resolve the participating ranks from the collective's group_name (already carried by the tensorrt::fused_nccl_* custom ops) and pass them to add_dist_collective, setting num_ranks = len(groups). Falls back to the world group when the group can't be resolved. Threads group_name from each fused-op converter (all_gather/reduce_scatter/all_reduce/all_to_all/scatter/gather). Validated on an 8xA100 CP4xTP2 run (LTX-2.3 DiT): supplying the CP subgroup's ranks to add_dist_collective clears the divisibility build error for the Ulysses all_to_all. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
… (cast-boundary) Adds, on top of pytorch#4380 (subgroup routing), the changes needed to lower the sharded LTX-2.3 DiT to a single TensorRT engine with native in-engine collectives: 1. _coll_boundary(): insert a genuine dtype reformat (fp32 by default, _COLL_BOUNDARY_DTYPE) on each DistCollective's input/output. Myelin folds identity/same-dtype casts away, so only a real reformat keeps the collective out of a ForeignNode -> the 48-block DiT compiles to one engine with collectives intact. See pytorch#4381. 2. Set layer.num_ranks BEFORE layer.get_output(0) so Myelin can infer the collective output shape (all_gather dim0 = in_dim0 * num_ranks); upstream set it after, tripping "AllGather requires world_size (>1) ... call myelinGraphSetWorldSize()". 3. AG_VIA_ALLREDUCE=1 (opt-in): express all_gather as place-into-slot + ALL_REDUCE(SUM) whose output shape == input shape, so no build-time world_size is needed. Numerically identical to all_gather. Also adds _mark_collective_boundary() (env TRT_COLL_BOUNDARY) as a debug-mark helper. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
Hi @pkisfaludi-nv! Thank you for your pull request and welcome to our community. Action RequiredIn order to merge any pull request (code, docs, etc.), we require contributors to sign our Contributor License Agreement, and we don't seem to have one on file for you. ProcessIn order for us to review and merge your suggested changes, please sign at https://code.facebook.com/cla. If you are contributing on behalf of someone else (eg your employer), the individual CLA may not be sufficient and your employer may need to sign the corporate CLA. Once the CLA is signed, our tooling will perform checks and validations. Afterwards, the pull request will be tagged with If you have received this in error or have any questions, please contact us at cla@meta.com. Thanks! |
|
We reproduced the follow-up The engine does not derive The core fix accumulates the maximum rank count across all MD-aware operations: mNbRanks = std::max(mNbRanks, params.nbRanks);
mNbRanks = std::max(mNbRanks, contextParallelDegree);Internal TensorRT MRs created with The focused TensorRT regression constructs Related CP4xTP2 subgroup-routing MRs are:
PR #4388's precision boundary is still useful: it prevents the unsupported I also added a Torch-TensorRT two-rank regression test in commit |
DRAFT — do not merge (author reviews). Depends on #4380.
Summary
On top of #4380 (which routes native NCCL collectives at the op's process subgroup), this adds the remaining changes that let the sharded LTX-2.3 DiT lower to a single TensorRT engine with native in-engine collectives (no TRT-LLM, no ONNX, no NCCL plugin). All changes are in
py/torch_tensorrt/dynamo/conversion/impl/nccl_ops.py.The three changes
_coll_boundary(). Myelin fuses a nativeIDistCollectiveLayerinto aForeignNodeand then fails withCollectiveOperation ... not supported In toMyelinCommKind(see Native DistCollective fails at build time when fused into a Myelin ForeignNode (large multi-device graph) #4381). Inserting a genuine dtype reformat (fp32 by default,_COLL_BOUNDARY_DTYPE) on the collective's input/output creates a precision-change boundary Myelin will not fuse across (identity / same-dtype casts get folded away — only a real reformat works). This lets the full 48-block DiT compile to one engine with collectives intact.num_ranksbeforeget_output().layer.num_ranksmust be set beforelayer.get_output(0)so Myelin can infer the collective's output shape (e.g. all_gatherdim0 = in_dim0 * num_ranks). Upstream sets it after, trippingAllGather requires world_size (>1) to be set for shape inference ... Call myelinGraphSetWorldSize().AG_VIA_ALLREDUCE=1(opt-in). Even with (2), TRT-11 Myelin can reject a fused native ALL_GATHER shape (needs the graph world_size, which torch-tensorrt never sets at build). The opt-in workaround expresses all_gather as scatter-this-rank's-slice-into-a-zero-padded-buffer thenALL_REDUCE(SUM)— numerically identical, and ALL_REDUCE's output shape == input shape so no build-time world_size is needed. Used for the video→audio KV gather in CP.Also adds
_mark_collective_boundary()(envTRT_COLL_BOUNDARY) as a debug-mark helper.Relationship to #4380 / #4381
_collective_group_ranks(group_name, ...)group resolution and is purely additive; the diff currently also shows dynamo: target native NCCL collectives at the op's process (sub)group #4380's commit and will shrink to just these changes once dynamo: target native NCCL collectives at the op's process (sub)group #4380 lands.toMyelinCommKind), delivered in the internal TensorRT/Myelin MRs (not in this repo).Validation
trt_engine_submodules=1,torch_coll_tokens=0(zero PyTorch-side collectives),ENGINE_RAN_OK; parity vs eager CP8 cos 0.99602 (video) / 0.99965 (audio) — PASS. (8× A100-SXM4-80GB.)Notes
AG_VIA_ALLREDUCEare env-gated and default-safe (world-group / no-op) so CP8 and existing single-group paths are unchanged.