From 047cbc3726ed95e6baf2403a8c2a1f8e7e467da1 Mon Sep 17 00:00:00 2001 From: Binyang Li Date: Thu, 16 Jul 2026 05:46:05 +0000 Subject: [PATCH] address comments --- python/mscclpp/ep/README.md | 15 +- python/mscclpp/ep/__init__.py | 8 +- python/mscclpp/ep/communicator.py | 10 +- python/mscclpp/ep/high_throughput.py | 12 +- python/mscclpp/ep/types.py | 10 +- src/ext/ep/bindings.cpp | 1 - src/ext/ep/include/api.cuh | 4 +- src/ext/ep/low_latency/combine.cu | 18 +- src/ext/ep/low_latency/dispatch.cu | 174 ++++--- test/python/ep/test_low_latency_multirank.py | 473 ++++++++++++------- 10 files changed, 445 insertions(+), 280 deletions(-) diff --git a/python/mscclpp/ep/README.md b/python/mscclpp/ep/README.md index d6c5da1d..968c5e0f 100644 --- a/python/mscclpp/ep/README.md +++ b/python/mscclpp/ep/README.md @@ -180,8 +180,6 @@ Use `DispatchLayout` instead of string literals for this field: | `DispatchLayout.TOKEN_MAJOR` | HT: `[total_recv_tokens, hidden]`; LL: `[world_size * max_tokens_per_rank, hidden]` | | `DispatchLayout.EXPERT_MAJOR` | `[num_local_experts, max_slots_per_expert, hidden]` | -`DispatchLayout.FLAT` is a compatibility alias for `DispatchLayout.TOKEN_MAJOR`. - ## MoECommunicator methods ```python @@ -267,7 +265,6 @@ class QuantConfig: class DispatchLayout(str, Enum): EXPERT_MAJOR = "expert_major" TOKEN_MAJOR = "token_major" - FLAT = TOKEN_MAJOR @dataclass @@ -317,11 +314,11 @@ class TokenMajorCombineContext: @dataclass -class RowMajorCombineContext: +class HighThroughputCombineContext: ... -CombineContext = ExpertMajorCombineContext | TokenMajorCombineContext | RowMajorCombineContext +CombineContext = ExpertMajorCombineContext | TokenMajorCombineContext | HighThroughputCombineContext class DispatchHandle: @@ -338,8 +335,8 @@ class TokenMajorDispatchHandle(DispatchHandle): combine_context: TokenMajorCombineContext -class RowMajorDispatchHandle(DispatchHandle): - combine_context: RowMajorCombineContext +class HighThroughputDispatchHandle(DispatchHandle): + combine_context: HighThroughputCombineContext @dataclass @@ -428,7 +425,7 @@ to reverse dispatch and finish combine. `ExpertMajorDispatchHandle` uses `ExpertMajorCombineContext` (`topk_ids`, `weights`, source info, layout ranges, shape, and capacity). `TokenMajorDispatchHandle` records source-token IDs, per-source-rank counts, and the original routing needed for cross-rank combine. -Row-major handles use the intranode combine context with +High-throughput handles use the intranode combine context with receive-side weights, source indices, prefix matrices, and send-head tensors. The MLP should treat the handle as opaque and pass it back to `combine`. @@ -563,7 +560,7 @@ dispatch_out.topk_ids # [world_size * max_tokens_per_rank, K], int32 lo dispatch_out.weights # [world_size * max_tokens_per_rank, K], float32 ``` -Non-local top-k entries use expert ID `-1`. The valid row count in each +Non-local top-k entries use expert ID `-1` and weight `0`. The valid row count in each source-rank region is returned in `dispatch_out.layout.num_tokens_per_rank`. For expert-major output, only the first `dispatch_out.layout.num_tokens_per_expert[i]` slots are valid: diff --git a/python/mscclpp/ep/__init__.py b/python/mscclpp/ep/__init__.py index e680bb15..948b2af5 100644 --- a/python/mscclpp/ep/__init__.py +++ b/python/mscclpp/ep/__init__.py @@ -22,13 +22,13 @@ from .communicator import ( # noqa: F401 DispatchOutputInfo, ExpertMajorDispatchHandle, ExpertMajorCombineContext, + HighThroughputDispatchHandle, + HighThroughputCombineContext, MoECommunicator, MoECommunicatorConfig, MoEMode, OperationOverlapConfig, QuantConfig, - RowMajorDispatchHandle, - RowMajorCombineContext, TokenMajorDispatchHandle, TokenMajorCombineContext, ) @@ -46,13 +46,13 @@ __all__ = [ "DispatchOutputInfo", "ExpertMajorDispatchHandle", "ExpertMajorCombineContext", + "HighThroughputDispatchHandle", + "HighThroughputCombineContext", "MoECommunicator", "MoECommunicatorConfig", "MoEMode", "OperationOverlapConfig", "QuantConfig", - "RowMajorDispatchHandle", - "RowMajorCombineContext", "TokenMajorDispatchHandle", "TokenMajorCombineContext", ] diff --git a/python/mscclpp/ep/communicator.py b/python/mscclpp/ep/communicator.py index c42f77c9..08d7e82a 100644 --- a/python/mscclpp/ep/communicator.py +++ b/python/mscclpp/ep/communicator.py @@ -21,11 +21,11 @@ from .types import ( DispatchOutputInfo, ExpertMajorDispatchHandle, ExpertMajorCombineContext, + HighThroughputDispatchHandle, + HighThroughputCombineContext, MoECommunicatorConfig, OperationOverlapConfig, QuantConfig, - RowMajorDispatchHandle, - RowMajorCombineContext, TokenMajorDispatchHandle, TokenMajorCombineContext, ) @@ -43,13 +43,13 @@ __all__ = [ "DispatchOutputInfo", "ExpertMajorDispatchHandle", "ExpertMajorCombineContext", + "HighThroughputDispatchHandle", + "HighThroughputCombineContext", "MoECommunicator", "MoECommunicatorConfig", "MoEMode", "OperationOverlapConfig", "QuantConfig", - "RowMajorDispatchHandle", - "RowMajorCombineContext", "TokenMajorDispatchHandle", "TokenMajorCombineContext", ] @@ -166,7 +166,7 @@ def _validate_common_config(config: MoECommunicatorConfig) -> None: def _resolve_output_layout(layout: Optional[DispatchLayout], mode: MoEMode) -> DispatchLayout: if layout is None: - return DispatchLayout.EXPERT_MAJOR if mode == MoEMode.LOW_LATENCY else DispatchLayout.FLAT + return DispatchLayout.EXPERT_MAJOR if mode == MoEMode.LOW_LATENCY else DispatchLayout.TOKEN_MAJOR if not isinstance(layout, DispatchLayout): raise TypeError("MoECommunicatorConfig.output_layout must be a DispatchLayout") return layout diff --git a/python/mscclpp/ep/high_throughput.py b/python/mscclpp/ep/high_throughput.py index f281ea96..c48dce96 100644 --- a/python/mscclpp/ep/high_throughput.py +++ b/python/mscclpp/ep/high_throughput.py @@ -24,10 +24,10 @@ from .types import ( DispatchLayoutInfo, DispatchOutput, DispatchOutputInfo, + HighThroughputCombineContext, + HighThroughputDispatchHandle, MoECommunicatorConfig, QuantConfig, - RowMajorCombineContext, - RowMajorDispatchHandle, ) from .utils import ( bf16_view as _bf16_view, @@ -397,7 +397,7 @@ class HighThroughputBackend: recv_topk_idx = cache["recv_topk_idx"] recv_topk_weights = cache["recv_topk_weights"] num_recv_tokens_per_expert_list = cache["num_recv_tokens_per_expert_list"] - combine_context = RowMajorCombineContext( + combine_context = HighThroughputCombineContext( recv_topk_weights=recv_topk_weights, src_idx=recv_src_idx, rank_prefix_matrix=rank_prefix_matrix, @@ -430,7 +430,7 @@ class HighThroughputBackend: None, self.expert_alignment, ) - combine_context = RowMajorCombineContext( + combine_context = HighThroughputCombineContext( recv_topk_weights=recv_topk_weights, src_idx=recv_src_idx, rank_prefix_matrix=rank_prefix_matrix, @@ -471,7 +471,7 @@ class HighThroughputBackend: topk_ids=recv_topk_idx, weights=recv_topk_weights, ) - handle = RowMajorDispatchHandle(output_info=output_info, combine_context=combine_context) + handle = HighThroughputDispatchHandle(output_info=output_info, combine_context=combine_context) # The torch-free HT runtime orders its work on the caller's CUDA stream # (no separate event handle), so there is nothing to attach here. handle._event = None # type: ignore[attr-defined] @@ -547,7 +547,7 @@ class HighThroughputBackend: raise ValueError("weights shape must match topk_ids") def _validate_combine_inputs(self, expert_output, handle) -> None: - if not isinstance(handle, RowMajorDispatchHandle): + if not isinstance(handle, HighThroughputDispatchHandle): raise TypeError("handle must be a DispatchHandle returned by dispatch") if expert_output.dim() != 2 or not expert_output.is_contiguous(): raise ValueError("expert_output must be a contiguous [total_recv_tokens, hidden] tensor") diff --git a/python/mscclpp/ep/types.py b/python/mscclpp/ep/types.py index 6bda6304..6c53b652 100644 --- a/python/mscclpp/ep/types.py +++ b/python/mscclpp/ep/types.py @@ -131,8 +131,8 @@ class TokenMajorCombineContext: @dataclass -class RowMajorCombineContext: - """Combine context for row-major high-throughput dispatch output.""" +class HighThroughputCombineContext: + """Combine context for high-throughput dispatch output.""" recv_topk_weights: Optional[torch.Tensor] src_idx: torch.Tensor @@ -141,7 +141,7 @@ class RowMajorCombineContext: send_head: torch.Tensor -CombineContext = Union[ExpertMajorCombineContext, TokenMajorCombineContext, RowMajorCombineContext] +CombineContext = Union[ExpertMajorCombineContext, TokenMajorCombineContext, HighThroughputCombineContext] # Opaque dispatch handles returned by dispatch() and consumed by combine(). @@ -165,8 +165,8 @@ class TokenMajorDispatchHandle(DispatchHandle): @dataclass -class RowMajorDispatchHandle(DispatchHandle): - combine_context: RowMajorCombineContext +class HighThroughputDispatchHandle(DispatchHandle): + combine_context: HighThroughputCombineContext # Optional async/overlap configuration. diff --git a/src/ext/ep/bindings.cpp b/src/ext/ep/bindings.cpp index 0e5a68c3..d28411e1 100644 --- a/src/ext/ep/bindings.cpp +++ b/src/ext/ep/bindings.cpp @@ -46,7 +46,6 @@ NB_MODULE(mscclpp_ep_cpp, m) { nb::enum_(m, "DispatchLayout") .value("EXPERT_MAJOR", mscclpp::ep::DispatchLayout::EXPERT_MAJOR) - .value("FLAT", mscclpp::ep::DispatchLayout::FLAT) .value("TOKEN_MAJOR", mscclpp::ep::DispatchLayout::TOKEN_MAJOR); nb::enum_(m, "CombineMode") diff --git a/src/ext/ep/include/api.cuh b/src/ext/ep/include/api.cuh index a4f41b56..e6ae4e44 100644 --- a/src/ext/ep/include/api.cuh +++ b/src/ext/ep/include/api.cuh @@ -32,9 +32,7 @@ enum class DispatchLayout { /// Token-major rows. Low latency uses /// [num_ranks * max_tokens_per_rank, hidden], grouped by source rank; high /// throughput uses [num_recv_tokens, hidden]. - TOKEN_MAJOR, - /// Backward-compatible alias for TOKEN_MAJOR. - FLAT = TOKEN_MAJOR + TOKEN_MAJOR }; // =========================================================================== diff --git a/src/ext/ep/low_latency/combine.cu b/src/ext/ep/low_latency/combine.cu index a117c93f..cab3c625 100644 --- a/src/ext/ep/low_latency/combine.cu +++ b/src/ext/ep/low_latency/combine.cu @@ -47,13 +47,16 @@ MSCCLPP_HOST_DEVICE_INLINE int directSendWorkerCount(int nLocalExperts) { return availableWorkers < DirectSendMaxNWorkers ? availableWorkers : DirectSendMaxNWorkers; } -template +template MSCCLPP_HOST_DEVICE_INLINE size_t combineSharedBytes(int nLocalExperts) { constexpr size_t TileBytes = static_cast(Hidden) * sizeof(Bf16); if constexpr (Mode == low_latency::CombineMode::DIRECT_SEND) { return directSendControlBytes(nLocalExperts) + static_cast(directSendWorkerCount(nLocalExperts)) * directSendWorkerBytes(); } + if constexpr (Layout == DispatchLayout::TOKEN_MAJOR) { + return CombineNStages * TileBytes; + } return CombineNStages * TileBytes; } @@ -188,10 +191,9 @@ MSCCLPP_DEVICE_INLINE void sendRankReducedPartials(const void* expertOutput, int } template