[Common][PyTorch] EP dispatch with unfused MXFP8 quantization - #3270
[Common][PyTorch] EP dispatch with unfused MXFP8 quantization#3270phu0ngng wants to merge 9 commits into
Conversation
Greptile SummaryAdds unfused MXFP8 quantization support to NCCL expert-parallel dispatch and combine backward.
Confidence Score: 5/5The PR appears safe to merge. No blocking failure remains. Important Files Changed
Sequence DiagramsequenceDiagram
participant User
participant PyEP as PyTorch EP
participant Quant as MXFP8 Quantizer
participant Bind as C++ Binding
participant Backend as NCCL EP Backend
User->>PyEP: ep_dispatch(BF16 tokens)
PyEP->>Quant: quantize tokens
Quant-->>PyEP: E4M3 data + E8M0 scales
PyEP->>Bind: dispatch(data, scales)
Bind->>Backend: route payload and scale rows
Backend-->>PyEP: expert-major data + scales
PyEP-->>User: GroupedTensor
User->>PyEP: ep_combine backward
PyEP->>Quant: quantize result gradient
PyEP->>Backend: reverse dispatch(data, scales)
Backend-->>User: grouped expert-output gradient
Reviews (4): Last reviewed commit: "bump nccl ep" | Re-trigger Greptile |
|
/te-ci L1 pytorch |
There was a problem hiding this comment.
Nope. Let me remove this change.
| (MXFP8 for now), ``tokens`` is the quantized tensor kept as the autograd operand so grad | ||
| reaches the pre-quant input. Recv outputs are carved/allocated here: a caller may supply | ||
| ``recv_tokens`` / ``recv_topk_weights``, else they are sized to ``rows``.""" | ||
| from .quantized_tensor import QuantizedTensor |
There was a problem hiding this comment.
nit: why not import at top?
| # Host mirror of total_recv_tokens, set by ep_prepare in eager mode. | ||
| self._host_total_recv_tokens: Optional[int] = None | ||
| # Set by ep_prepare; dispatch/combine read the routing it cached in handle_mem. | ||
| self.prepared = False |
There was a problem hiding this comment.
Seems this is only in the raw functions for assertion? What is the usage?
There was a problem hiding this comment.
For assertion only, but actually not very useful, so I will remove it.
| return torch.empty(*shape, dtype=dtype, device=device) | ||
|
|
||
|
|
||
| def _quantize(x: torch.Tensor, recipe): |
There was a problem hiding this comment.
I feel it would be better to find a better home for these quantization functions, seems to me they do not EP specific, or the requirement is implicit.
There was a problem hiding this comment.
Removed the helper and inlined the impl.
| if buffer.dispatch_fwd_quant_recipe is not None: | ||
| # The grouped recv's packed scales only match the grouped layout when each expert slot is | ||
| # 128-aligned. | ||
| if buffer.alignment <= 0 or buffer.alignment % 128 != 0: |
There was a problem hiding this comment.
Is this a requirement for NCCL EP? The downstream group gemms have their own asserts
There was a problem hiding this comment.
This is not a requirement from NCCL EP.
You are right that it's the job of the GEMM ops to enforce their alignment requirements.
| return _SYMM_MEM_POOL | ||
|
|
||
|
|
||
| def release_symm_mem_pool(device: Optional[torch.device] = None) -> None: |
There was a problem hiding this comment.
This is great, shall we also call it in ep_finalize?
There was a problem hiding this comment.
release_symm_mem_pool is only required before destroy_process_group().
User may call ep_finalize without destroy_process_group().
There was a problem hiding this comment.
Hmm why is that? I did not have it in mcore but it seems working (could be luck)
There was a problem hiding this comment.
Also just see input device is never used, shall we remove?
There was a problem hiding this comment.
On why
release_symm_mem_pool is only required before destroy_process_group().
I think destroy_process_group() deletes the NCCL communicator that is used to create the symmem, so the cached symmem-s become limbo.
Are you calling destroy_process_group() in MCore? Why?
Signed-off-by: Phuong Nguyen <phuonguyen@nvidia.com>
Signed-off-by: Phuong Nguyen <phuonguyen@nvidia.com>
Signed-off-by: Phuong Nguyen <phuonguyen@nvidia.com>
Signed-off-by: Phuong Nguyen <phuonguyen@nvidia.com>
…der CUDA graph capture Signed-off-by: Phuong Nguyen <phuonguyen@nvidia.com>
Signed-off-by: Phuong Nguyen <phuonguyen@nvidia.com>
…CUDA-graph capture Signed-off-by: Phuong Nguyen <phuonguyen@nvidia.com>
Signed-off-by: Phuong Nguyen <phuonguyen@nvidia.com>
Signed-off-by: Phuong Nguyen <phuonguyen@nvidia.com>
| torch.ops.transformer_engine_ep.combine_bwd(handle_mem, g_result, grad_expert_out) | ||
| else: | ||
| if tex.ep_get_zero_copy(): | ||
| raise NotImplementedError( |
There was a problem hiding this comment.
What is the limitation here?
There was a problem hiding this comment.
Stale. Let me remove it.
Description
This PR adds MXFP8 support to the dispatch op of the NCCL EP path. The dispatch op is used in two places, and MXFP8 applies to both:
GroupedTensor.GroupedTensor.Type of change
Changes
PyTorch frontend (
transformer_engine/pytorch/ep.py,distributed.py,csrc/extensions/ep.cpp)**dispatch_quant_recipeis set (MXFP8BlockScalingonly for now); dispatch-forward recv is returned as a per-expertGroupedTensor. A pre-quantized input is rejected.GroupedTensor. Combine forward is unchanged (high-precision).Common backend (
common/ep/ep_backend.cpp,include/.../ep.h,comm_window.h)**NCCL EP submodule**
3rdparty/nccl-extensionsto the revision providing block-scaled dispatch.Tests (
tests/cpp_distributed/test_ep.cu,tests/pytorch/distributed/run_ep.py,run_test_ep.sh)**NVTE_EP_MXFP8_PASSrun since the grouped path pins the per-expert alignment process-wide.Checklist: