/* * Portions of this file are adapted from DeepEP (https://github.com/deepseek-ai/DeepEP). * Copyright (c) 2025 DeepSeek. Licensed under the MIT License. * SPDX-License-Identifier: MIT */ /* * SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. * SPDX-License-Identifier: Apache-2.0 * See LICENSE.txt for more license information. */ #include #include "nccl_device.h" #include "device_primitives.cuh" namespace cg = cooperative_groups; namespace nccl_ep { namespace internode_ll { template __forceinline__ __device__ bool isRankMasked(int* rankMask, int rank) { if (rankMask == nullptr) { return false; } if constexpr (useWarpSync) { return __shfl_sync(0xffffffff, ld_acquire_global(rankMask + rank), 0) != 0; } else { return ld_acquire_global(rankMask + rank) != 0; } } __device__ __constant__ bool dP2pDisabled = false; __forceinline__ __device__ uint64_t ncclGetP2pPtr(const uint64_t& dstPtr, const size_t& offset, const int& rank, const int& dstRank, const int& expertIdx, const ncclWindow_t* ncclWindows, ncclDevComm* devComms) { // Local rank, no need for peer mapping if (rank == dstRank) { return dstPtr; } // If P2P is globally disabled, always use RDMA path if (dP2pDisabled) { return 0; } // P2P/NVLink only works between ranks on the same node (LSA team) // Use NCCL team APIs to check if dstRank is in the same LSA team. // Always use commId=0: single devComm with all GIN contexts (1-comm N-context design). constexpr int commId = 0; ncclTeam lsa = ncclTeamLsa(devComms[commId]); ncclTeam world = ncclTeamWorld(devComms[commId]); if (!ncclTeamRankIsMember(lsa, world, dstRank)) return 0; // Different nodes (not in same LSA team), must use RDMA auto const p2pPtr = reinterpret_cast(ncclGetPeerPointer(ncclWindows[commId], offset, dstRank)); return p2pPtr ? p2pPtr : 0; } // ============================================================================ // Helper functions for dispatch kernel modularization // ============================================================================ // Cast BF16 data to FP8 and write to send buffer, or copy BF16 data directly template __forceinline__ __device__ void castAndWriteToSendBuf( const int4* srcDataInt4, typename std::conditional::type* sendBufVec, float* sendBufScales, int threadId, int numThreads, int laneId, int hiddenBf16Int4, bool roundScale) { constexpr int kNumElemsPerRead = sizeof(int4) / sizeof(nv_bfloat16); constexpr int kNumPerChannels = 128; EP_DEVICE_ASSERT(hiddenBf16Int4 % 32 == 0); #pragma unroll for (int i = threadId; i < hiddenBf16Int4; i += numThreads) { auto dataInt4 = __ldg(srcDataInt4 + i); if constexpr (kUseFP8) { // Calculate local amax auto bf16Data = reinterpret_cast(&dataInt4); float fp32Data[kNumElemsPerRead]; float amax = kFP8Margin, scale, scaleInv; #pragma unroll for (int j = 0; j < kNumElemsPerRead; ++j) { fp32Data[j] = static_cast(bf16Data[j]); amax = fmaxf(amax, fabsf(fp32Data[j])); } // Reduce amax and scale EP_STATIC_ASSERT(kNumElemsPerRead * 32 / kNumPerChannels == 2, "Invalid vectorization"); amax = warp_reduce_max<16>(amax); calculate_fp8_scales(amax, scale, scaleInv, roundScale); if (laneId == 0 or laneId == 16) sendBufScales[i * kNumElemsPerRead / 128] = scaleInv; // Cast into send buffer typename std::conditional::type dataInt2; auto fp8x2Data = reinterpret_cast<__nv_fp8x2_storage_t*>(&dataInt2); #pragma unroll for (int j = 0; j < kNumElemsPerRead; j += 2) { float2 fp32x2 = {fp32Data[j] * scale, fp32Data[j + 1] * scale}; fp8x2Data[j / 2] = __nv_cvt_float2_to_fp8x2(fp32x2, __NV_SATFINITE, __NV_E4M3); } sendBufVec[i] = dataInt2; } else { // Reinterpret-cast is for C++14 compatibility sendBufVec[i] = *reinterpret_cast::type*>(&dataInt4); } } } // Send a token via RDMA or P2P __forceinline__ __device__ void sendToken( const int* sendBufSrcIdx, uint64_t recvPtr, size_t expectedDstOffset, int tokenIdx, size_t sendOff, size_t numBytesPerMsg, int dstRank, int dstExpertLocalIdx, int currRank, int* rankMask, const ncclWindow_t* windows, ncclDevComm* devComms, int laneId, size_t numInt4PerMsg) { const auto dstP2pPtr = ncclGetP2pPtr(recvPtr, expectedDstOffset, currRank, dstRank, dstExpertLocalIdx, windows, devComms); if (not isRankMasked(rankMask, dstRank)) { if (dstP2pPtr == 0) { if (laneId == 0) { size_t expectedSrcOffset = sendOff + tokenIdx * numBytesPerMsg; constexpr int commId = 0; auto ctxId = dstExpertLocalIdx % MAX_NCCL_GIN_CTX_PER_COMM; ncclGin net(devComms[commId], ctxId); ncclTeam world = ncclTeamWorld(devComms[commId]); auto ncclWindow = windows[commId]; net.put(world, dstRank, ncclWindow, expectedDstOffset, ncclWindow, expectedSrcOffset, numBytesPerMsg, ncclGin_None{}, // no signal ncclGin_None{}, // no counter ncclCoopThread()); } } else { // NOTES: only 2 load iterations for 7K hidden with 8 unrolls // Copy entire message including index from sendBufSrcIdx to dstP2pPtr const auto* sendDataInt4 = reinterpret_cast(sendBufSrcIdx); const auto* recvDataInt4 = reinterpret_cast(dstP2pPtr); UNROLLED_WARP_COPY(8, laneId, numInt4PerMsg, recvDataInt4, sendDataInt4, ld_nc_global, st_na_global); } } } // Clean next receive count buffer __forceinline__ __device__ void cleanNextRecvCntBuf( int* nextRecvCntBuf, int nextRecvCntBufSize, int laneId) { if (!dP2pDisabled) { #pragma unroll for (int i = laneId; i < nextRecvCntBufSize; i += 32) nextRecvCntBuf[i] = 0; } } // Count tokens per expert from topk indices __forceinline__ __device__ void countTokensPerExpert( const int64_t* inTopkIdx, int numTokens, int numTopk, int expertBeginIdx, int expertEndIdx, int* expertCount, int* sharedNumTokensSentPerExpert, int* expertDone, int laneId) { // Per lane count #pragma unroll 8 for (int i = laneId; i < numTokens * numTopk; i += 32) { auto idx = static_cast(__ldg(inTopkIdx + i)); if (idx >= expertBeginIdx and idx < expertEndIdx) expertCount[idx - expertBeginIdx]++; } // Warp reduce #pragma unroll for (int i = expertBeginIdx; i < expertEndIdx; ++i) { auto sum = warp_reduce_sum(expertCount[i - expertBeginIdx]); if (laneId == 0) { sharedNumTokensSentPerExpert[i - expertBeginIdx] = sum; atomic_add_release_global(expertDone + i, FINISHED_SUM_TAG - sum); } } } // Send expert count to destination rank __forceinline__ __device__ void sendExpertCount( int numTokensSent, int dstRank, int dstExpertLocalIdx, int currRank, int numRanks, uint64_t recvCntPtr, size_t recvCntOffset, int* recvCntBuf, int* rankMask, unsigned signalsBase, const ncclWindow_t* windows, ncclDevComm* devComms) { const auto dstP2pPtr = ncclGetP2pPtr(recvCntPtr, recvCntOffset, currRank, dstRank, dstExpertLocalIdx, windows, devComms); if (not isRankMasked(rankMask, dstRank)) { if (dstP2pPtr == 0) { constexpr int commId = 0; auto ctxId = dstExpertLocalIdx % MAX_NCCL_GIN_CTX_PER_COMM; auto signalId = signalsBase + dstExpertLocalIdx * numRanks + currRank; ncclGin net(devComms[commId], ctxId); ncclTeam world = ncclTeamWorld(devComms[commId]); auto ncclWindow = windows[commId]; net.put(world, dstRank, ncclWindow, recvCntOffset, ncclWindow, 0, 0, // 0 bytes transfer ncclGin_SignalAdd{signalId, static_cast(numTokensSent) + 1}, ncclGin_None{}, // no counter ncclCoopThread()); } else { st_release_sys_global(reinterpret_cast(dstP2pPtr), -numTokensSent - 1); } } } // Wait for receive tokens to arrive and return count __forceinline__ __device__ int waitForRecvTokens( int srcRank, int localExpertIdx, int currRank, int numRanks, size_t recvCntOff, int* recvCntBuf, int* rankMask, unsigned signalsBase, const ncclWindow_t* windows, ncclDevComm* devComms, int* recvStats, int64_t* waitStats) { auto startTime = clock64(); uint64_t waitRecvCost = 0; int numRecvTokens = 0; if (not isRankMasked(rankMask, srcRank)) { size_t srcOffset = recvCntOff + (localExpertIdx * numRanks + srcRank) * sizeof(int); auto srcP2pPtr = ncclGetP2pPtr(0x01, srcOffset, currRank, srcRank, localExpertIdx, windows, devComms); if (srcP2pPtr == 0) { constexpr int commId = 0; auto ctxId = localExpertIdx % MAX_NCCL_GIN_CTX_PER_COMM; ncclGin net(devComms[commId], ctxId); uint64_t curValue; do { curValue = net.readSignal(signalsBase + localExpertIdx * numRanks + srcRank); } while (curValue < 1 // data not arrived && (waitRecvCost = clock64() - startTime) <= NUM_TIMEOUT_CYCLES // not timeout ); net.resetSignal(signalsBase + localExpertIdx * numRanks + srcRank); numRecvTokens = -(int)curValue; } else { while ((numRecvTokens = ld_acquire_sys_global((recvCntBuf + localExpertIdx * numRanks + srcRank))) == 0 // data not arrived && (waitRecvCost = clock64() - startTime) <= NUM_TIMEOUT_CYCLES // not timeout ); } } // Do not receive tokens if rank timeout or masked if (numRecvTokens == 0) numRecvTokens = -1; // Mask rank if timeout if (waitRecvCost > NUM_TIMEOUT_CYCLES) { printf("Warning: NCCL EP timeout for dispatch receive, rank %d, local_expert_idx %d, src_rank %d\n", currRank, localExpertIdx, srcRank); if (rankMask == nullptr) trap(); atomicExch(rankMask + srcRank, 1); } numRecvTokens = -numRecvTokens - 1; // Add stats for diagnosis if (recvStats != nullptr) atomicAdd(recvStats + localExpertIdx, numRecvTokens); if (waitStats != nullptr) atomicAdd(reinterpret_cast(waitStats + srcRank), waitRecvCost); return numRecvTokens; } // Copy received token data and scales template __forceinline__ __device__ void copyRecvTokenData( const uint8_t* recvBufUint8, int tokenIdx, int recvTokenBeginIdx, int4* outDataInt4, int* outSrcInfo, typename std::conditional::type* outScales, int hiddenInt4, int hiddenBytes, int numScales, int numBytesPerMsg, int maxTokensPerRank, int numRanks, int laneId) { using scale_t = std::conditional_t; using packed_t = std::conditional_t; // Copy source info const auto recvBufSrcIdx = reinterpret_cast(recvBufUint8 + tokenIdx * numBytesPerMsg); if (laneId == 0) outSrcInfo[recvTokenBeginIdx + tokenIdx] = ld_nc_global(recvBufSrcIdx); __syncwarp(); // Copy data // NOTES: only 2 load iterations for 7K hidden with 7 unrolls const auto recvDataInt4 = reinterpret_cast(reinterpret_cast(recvBufSrcIdx) + sizeof(int4)); const auto outDataInt4Ptr = outDataInt4 + (recvTokenBeginIdx + tokenIdx) * hiddenInt4; UNROLLED_WARP_COPY(7, laneId, hiddenInt4, outDataInt4Ptr, recvDataInt4, ld_nc_global, st_na_global); // Copy scales if constexpr (kUseFP8) { // Equivalent CuTe layout: // (num_tokens, (num_packed, num_elems_per_pack)):(num_elems_per_pack, (num_tokens * num_elems_per_pack, 1)) const auto recvScales = reinterpret_cast(reinterpret_cast(recvDataInt4) + hiddenBytes); const auto numElemsPerPack = static_cast(sizeof(packed_t) / sizeof(scale_t)); const auto tokenIdxFinal = recvTokenBeginIdx + tokenIdx; const auto tokenStride = numElemsPerPack; const auto packStride = numRanks * maxTokensPerRank * numElemsPerPack; if (laneId < numScales) { const auto packIdx = laneId / numElemsPerPack; const auto elemIdx = laneId % numElemsPerPack; auto scale = extract_required_scale_format(ld_nc_global(recvScales + laneId)); outScales[tokenIdxFinal * tokenStride + packIdx * packStride + elemIdx] = scale; } if (laneId + 32 < numScales) { const auto packIdx = (laneId + 32) / numElemsPerPack; const auto elemIdx = (laneId + 32) % numElemsPerPack; auto scale = extract_required_scale_format(ld_nc_global(recvScales + laneId + 32)); outScales[tokenIdxFinal * tokenStride + packIdx * packStride + elemIdx] = scale; } } } template __global__ __launch_bounds__(1024, 1) void dispatch(// INPUT const void* inData, const int64_t* inTopkIdx, int* rankMask, // OUTPUT void* outDataBuf, void* outScalesBuf, int* outSrcInfo, int64_t* outLayout, int* outCnt, // INTERMEDIATE void* sendBuf, void* recvBuf, int* recvCntBuf, size_t sendOff, size_t recvOff, size_t recvCntOff, int* expertCnt, int* expertDone, int* nextRecvCntBuf, int nextRecvCntBufSize, int* recvStats, int64_t* waitStats, // CONFIG int numTokens, int maxTokensPerRank, int numTopk, int numExperts, int currRank, int numRanks, int numWarpGroups, int numWarpsPerGroup, bool roundScale, int phases, int numComms, ncclDevComm* devComms, const ncclWindow_t* windows, unsigned signalsBase) { const auto smId = static_cast(blockIdx.x); const auto threadId = static_cast(threadIdx.x); const auto warpId = threadId / 32, laneId = get_lane_id(); const auto numSms = static_cast(gridDim.x); const auto numWarps = numWarpGroups * numWarpsPerGroup; const auto numLocalExperts = numExperts / numRanks; const auto warpGroupId = warpId / numWarpsPerGroup; const auto subWarpId = warpId % numWarpsPerGroup; const auto responsibleExpertIdx = smId * numWarpGroups + warpGroupId; // May extract UE8M0 from the scales using scale_t = std::conditional_t; using packed_t = std::conditional_t; EP_STATIC_ASSERT(sizeof(packed_t) % sizeof(scale_t) == 0, "Invalid vector length"); // FP8 staffs constexpr int kNumPerChannels = 128; const int numScales = kHidden / kNumPerChannels; const size_t hiddenBytes = kHidden * (kUseFP8 ? sizeof(__nv_fp8_storage_t) : sizeof(nv_bfloat16)); const size_t hiddenInt4 = hiddenBytes / sizeof(int4); // Message package: index at source (int), 3 reserved int fields, hidden data, FP8 scales // NOTES: currently we have 3 reserved int fields for future use using vec_t = std::conditional_t; const size_t numBytesPerMsg = sizeof(int4) + (kUseFP8 ? (kHidden + numScales * sizeof(float)) : (kHidden * sizeof(nv_bfloat16))); const size_t numInt4PerMsg = numBytesPerMsg / sizeof(int4); EP_DEVICE_ASSERT(numBytesPerMsg % sizeof(int4) == 0); // Expert counts constexpr int kNumMaxWarpGroups = 32; __shared__ int sharedNumTokensSentPerExpert[kNumMaxWarpGroups]; // Sending phase if ((phases & LOW_LATENCY_SEND_PHASE) == 0) goto LOW_LATENCY_DISPATCH_RECV; // There are 2 kinds of warps in this part: // 1. The first-kind warps for FP8 cast and sending top-k tokens // 2. The last warp for reading `topk_idx` and count for per-expert information if (warpId < numWarps - 1) { constexpr int kNumElemsPerRead = sizeof(int4) / sizeof(nv_bfloat16); EP_STATIC_ASSERT(kHidden % (32 * kNumElemsPerRead) == 0, "Invalid hidden"); EP_STATIC_ASSERT(kNumElemsPerRead * 32 % kNumPerChannels == 0, "Invalid vectorization"); const auto numThreads = (numWarps - 1) * 32; const size_t hiddenBf16Int4 = kHidden / kNumElemsPerRead; for (int tokenIdx = smId; tokenIdx < numTokens; tokenIdx += numSms) { const auto srcDataInt4 = static_cast(inData) + tokenIdx * hiddenBf16Int4; const auto sendBufSrcIdx = reinterpret_cast(static_cast(sendBuf) + tokenIdx * numBytesPerMsg); const auto sendBufVec = reinterpret_cast(reinterpret_cast(sendBufSrcIdx) + sizeof(int4)); const auto sendBufScales = reinterpret_cast(reinterpret_cast(sendBufVec) + hiddenBytes); // Overlap top-k index read and source token index writes auto dstExpertIdx = warpId < numTopk ? static_cast(__ldg(inTopkIdx + tokenIdx * numTopk + warpId)) : -1; threadId == 0 ? (*sendBufSrcIdx = tokenIdx) : 0; // Cast and write data to send buffer castAndWriteToSendBuf( srcDataInt4, sendBufVec, sendBufScales, threadId, numThreads, laneId, hiddenBf16Int4, roundScale); asm volatile("bar.sync 1, %0;" :: "r"(numThreads)); // Issue IBGDA sends if (dstExpertIdx >= 0) { int slotIdx = laneId == 0 ? atomicAdd(expertCnt + dstExpertIdx, 1) : 0; slotIdx = __shfl_sync(0xffffffff, slotIdx, 0); const auto dstRank = dstExpertIdx / numLocalExperts; const auto dstExpertLocalIdx = dstExpertIdx % numLocalExperts; const auto recvPtr = reinterpret_cast(recvBuf) + dstExpertLocalIdx * numRanks * maxTokensPerRank * numBytesPerMsg + currRank * maxTokensPerRank * numBytesPerMsg + slotIdx * numBytesPerMsg; size_t expectedDstOffset = recvOff + dstExpertLocalIdx * numRanks * maxTokensPerRank * numBytesPerMsg + currRank * maxTokensPerRank * numBytesPerMsg + slotIdx * numBytesPerMsg; sendToken( sendBufSrcIdx, recvPtr, expectedDstOffset, tokenIdx, sendOff, numBytesPerMsg, dstRank, dstExpertLocalIdx, currRank, rankMask, windows, devComms, laneId, numInt4PerMsg); // Increase counter after finishing __syncwarp(); laneId == 0 ? atomic_add_release_global(expertDone + dstExpertIdx, 1) : 0; } } } else if (warpId == numWarps - 1) { EP_DEVICE_ASSERT(numSms > 1); if (smId == 0) { cleanNextRecvCntBuf(nextRecvCntBuf, nextRecvCntBufSize, laneId); // Notify before executing `int_p` __syncwarp(); #pragma unroll for (int i = laneId; i < numExperts; i += 32) atomic_add_release_global(expertDone + i, FINISHED_SUM_TAG); } // Count tokens per expert from topk indices int expertCount[kNumMaxWarpGroups] = {0}; const auto expertBeginIdx = smId * numWarpGroups; const auto expertEndIdx = min(expertBeginIdx + numWarpGroups, numExperts); countTokensPerExpert( inTopkIdx, numTokens, numTopk, expertBeginIdx, expertEndIdx, expertCount, sharedNumTokensSentPerExpert, expertDone, laneId); } __syncthreads(); // Issue count sends if (responsibleExpertIdx < numExperts and subWarpId == 0 and laneId == 0) { const auto dstRank = responsibleExpertIdx / numLocalExperts; const auto dstExpertLocalIdx = responsibleExpertIdx % numLocalExperts; const auto numTokensSent = sharedNumTokensSentPerExpert[responsibleExpertIdx - smId * numWarpGroups]; // Wait local sends issued and send expert counts while (ld_acquire_global(expertDone + responsibleExpertIdx) != FINISHED_SUM_TAG * 2); auto recvCntPtr = reinterpret_cast(recvCntBuf + dstExpertLocalIdx * numRanks + currRank); size_t recvCntOffset = recvCntOff + (dstExpertLocalIdx * numRanks + currRank) * sizeof(int); sendExpertCount( numTokensSent, dstRank, dstExpertLocalIdx, currRank, numRanks, recvCntPtr, recvCntOffset, recvCntBuf, rankMask, signalsBase, windows, devComms); // Clean workspace for next use expertCnt[responsibleExpertIdx] = 0; expertDone[responsibleExpertIdx] = 0; // Clean `packed_recv_count` if (dstRank == 0) outCnt[dstExpertLocalIdx] = 0; } __syncwarp(); // Receiving phase LOW_LATENCY_DISPATCH_RECV: if ((phases & LOW_LATENCY_RECV_PHASE) == 0) return; // For send-and-recv kernels, we need a grid sync for making `packed_recv_count` visible if (phases & LOW_LATENCY_SEND_PHASE) cg::this_grid().sync(); // Receiving and packing if (responsibleExpertIdx < numExperts) { const auto srcRank = responsibleExpertIdx / numLocalExperts; const auto localExpertIdx = responsibleExpertIdx % numLocalExperts; const auto recvBufUint8 = static_cast(recvBuf) + localExpertIdx * numRanks * maxTokensPerRank * numBytesPerMsg + srcRank * maxTokensPerRank * numBytesPerMsg; const auto outDataInt4 = static_cast(outDataBuf) + localExpertIdx * numRanks * maxTokensPerRank * hiddenInt4; const auto recvSrcInfo = outSrcInfo + localExpertIdx * numRanks * maxTokensPerRank; const auto recvRange = outLayout + localExpertIdx * numRanks; const auto numAlignedScales = align(numScales, sizeof(float) / sizeof(scale_t)); const auto outScales = static_cast(outScalesBuf) + localExpertIdx * numRanks * maxTokensPerRank * numAlignedScales; // Shared between sub-warps in warp groups __shared__ int sharedNumRecvTokens[kNumMaxWarpGroups], sharedRecvTokenBeginIdx[kNumMaxWarpGroups]; // Wait tokens to arrive // NOTES: using sub-warp 1 to overlap with sub-warp 0 int numRecvTokens, recvTokenBeginIdx; EP_DEVICE_ASSERT(numWarpsPerGroup > 1 and numWarpGroups < 15); if (subWarpId == 1 and laneId == 0) { numRecvTokens = waitForRecvTokens( srcRank, localExpertIdx, currRank, numRanks, recvCntOff, recvCntBuf, rankMask, signalsBase, windows, devComms, recvStats, waitStats); recvTokenBeginIdx = atomicAdd(outCnt + localExpertIdx, numRecvTokens); sharedNumRecvTokens[warpGroupId] = numRecvTokens; sharedRecvTokenBeginIdx[warpGroupId] = recvTokenBeginIdx; recvRange[srcRank] = pack2(numRecvTokens, recvTokenBeginIdx); } asm volatile("bar.sync %0, %1;" :: "r"(warpGroupId + 2), "r"(numWarpsPerGroup * 32)); numRecvTokens = sharedNumRecvTokens[warpGroupId]; recvTokenBeginIdx = sharedRecvTokenBeginIdx[warpGroupId]; // Copy tokens EP_DEVICE_ASSERT(numScales <= 64); for (int i = subWarpId; i < numRecvTokens; i += numWarpsPerGroup) { copyRecvTokenData( recvBufUint8, i, recvTokenBeginIdx, outDataInt4, recvSrcInfo, outScales, hiddenInt4, hiddenBytes, numScales, numBytesPerMsg, maxTokensPerRank, numRanks, laneId); } } } void dispatch(const void* inData, const int64_t* inTopkIdx, void* outDataBuf, void* outScalesBuf, int* outSrcInfo, int64_t* outLayout, int* outCnt, void* sendBuf, void* recvBuf, int* recvCntBuf, size_t sendOff, size_t recvOff, size_t recvCntOff, int* nextRecvCntBuf, int nextRecvCntBufSize, int* recvStats, int64_t* waitStats, int numTokens, int hidden, int maxTokensPerRank, int numTopk, int numExperts, int currRank, int numRanks, bool useFp8, bool roundScale, bool useUe8m0, int phases, int numComms, ncclDevComm* devComms, const ncclWindow_t* windows, unsigned signalsBase, void* workspace, int numDeviceSms, cudaStream_t stream) { constexpr int kNumMaxTopK = 9; const int numWarpGroups = ceil_div(numExperts, numDeviceSms); const int numWarpsPerGroup = 32 / numWarpGroups; EP_HOST_ASSERT(numWarpGroups > 0 and numWarpsPerGroup > 0); EP_HOST_ASSERT(kNumMaxTopK + 1 <= numWarpGroups * numWarpsPerGroup); const auto numWarps = numWarpGroups * numWarpsPerGroup; const auto numSms = ceil_div(numExperts, numWarpGroups); EP_HOST_ASSERT(numTopk <= kNumMaxTopK); // Workspace checks auto expertCnt = static_cast(workspace); auto expertDone = expertCnt + numExperts; EP_HOST_ASSERT(numExperts * sizeof(int) * 2 <= NUM_WORKSPACE_BYTES); // FP8 checks if (useUe8m0) EP_HOST_ASSERT(roundScale and "UE8M0 SF requires `round_scale=True`"); #define DISPATCH_LAUNCH_CASE(hidden) { \ auto dispatchFunc = dispatch