[MSCCLPP-EP] Python interface for the C++ expert-parallel runtime - #907
Binyang Li (Binyang2014) wants to merge 5 commits into
Conversation
Port the feature/ep PyTorch API to opaque preparation and dispatch handles, GPU-resident receive counts, and the current single-stream runtime. Link standalone nanobind bindings to the existing C++ EP library and package its native dependencies without linking libtorch. Stage external rank-major expert inputs after native handle validation to prevent stale combines from overwriting live runtime data. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
Azure Pipelines: There may be pipelines that require an authorized user to comment /azp run to run. |
| .dispatchDataType_ = dataType}; | ||
| } | ||
|
|
||
| const int* MoERuntime::numRecvTokensBuffer() const { |
There was a problem hiding this comment.
Why need to expose this API?
There was a problem hiding this comment.
The Python TOKEN_MAJOR result has a capacity-sized tensor, so GPU consumers need the actual number of valid received rows without copying a count to the CPU. The existing outputCount is per expert: a token routed to two local experts contributes two expert counts but occupies only one received row, so summing those counts is incorrect.
This accessor only borrows the scalar already allocated in the native workspace. It adds no allocation, count-computation kernel, or host synchronization; Python exposes it as read-only-by-contract layout.num_recv_tokens. I renamed it numRecvTokensDevicePtr() in 312b908 to make the device-pointer semantics clear. Leaving the public-interface design question open for your review.
Use a shared initialization decorator and leave graph capture timing and unsupported buffer aliasing to callers. Preserve allocator stream tracking and only the device guards needed for explicit device selection. Clarify expert-result ownership and name the borrowed receive-count accessor as a device pointer. Cover lazy initialization and device restoration, and expect the native MSCCL++ error type from the updated base. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
| """Fixed runtime configuration, which must agree across participating ranks. | ||
|
|
||
| Experts are partitioned evenly into contiguous rank-local ranges. A scalar | ||
| ``num_blocks=N`` resolves to ``(N, N - 2)`` for latency or ``(N, N)`` for |
There was a problem hiding this comment.
What's the N, N-2 meaning
| def record_stream(tensors: Iterable[Optional[torch.Tensor]], stream: torch.cuda.Stream) -> None: | ||
| """Protect allocator-backed storage used by native asynchronous GPU work. | ||
|
|
||
| PyTorch's allocator ignores foreign storage. Runtime buffers instead rely on | ||
| their native owner and the caller's obligation to finish local and peer use. | ||
| This does not provide producer/consumer ordering across streams. | ||
| """ | ||
| for tensor in tensors: | ||
| if tensor is not None: | ||
| tensor.record_stream(stream) |
There was a problem hiding this comment.
Remove this function?
| tensor.record_stream(stream) | ||
|
|
||
|
|
||
| class DevicePointerArray: |
There was a problem hiding this comment.
Why need this?
Remove the Python and public C++ receive-row count accessors and keep throughput row bounds in the native workspace. TOKEN_MAJOR remains capacity-sized, while native combine follows private routing metadata and ignores unused tail rows. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Require latency rank-major expert computation to use the runtime combine buffer instead of hiding a device-to-device staging copy. Clarify that throughput token-major rows are source-rank/token ordered while exposed expert counts are workload statistics. Consolidate Python EP tests into one end-to-end correctness matrix using top-k 8 and hidden size 4096 across every supported mode, layout, dtype, combine, and preparation path. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Summary
Stacked on #903 (
binyli/ep-throughput-cpp). Ports the PyTorch-facing EP interface fromfeature/ep(232c922d) onto the current C++ latency/throughput runtime, without copying the donor's kernels or legacy receive-pool implementation.Changes
mscclpp.eppackage withMoECommunicator, configuration/output types, explicitprepare, opaque preparation/dispatch handles, and tensor-based dispatch/combine.libmscclpp_ep. The native boundary uses raw device pointers and does not link libtorch; PyTorch is an optionalepdependency.Contract and scope
All work sharing a runtime uses one caller CUDA stream, including expert computation. There are no internal completion events, cross-stream overlap stubs, or automatic autograd integration. Runtime-owned views and handles retain storage, but callers must finish local and peer GPU use before releasing the final owner.
The underlying implementation requires CUDA SM90+ and 1-64 ranks in one IPC domain, with top-k up to 8. Physical 32/64-GPU qualification is not claimed. The donor's
previous_handle, overlap configuration, notify matrices, and receive-pool tuning are not carried into this API.Validation
MoERuntimeTestC++ cases passed on eight GPUs.