/* * 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. */ #pragma once // ============================================================================ // Configuration constants // ============================================================================ #define NUM_MAX_RDMA_PEERS 20 #define NUM_WORKSPACE_BYTES (32 * 1024 * 1024) #define NUM_MAX_LOCAL_EXPERTS 1024 #define NUM_BUFFER_ALIGNMENT_BYTES 128 #define MAX_HIDDEN_DIM 16384 #define MAX_NCCL_GIN_CTX_PER_COMM 4 #define FINISHED_SUM_TAG 1024 #define NUM_WAIT_NANOSECONDS 500 #define MAX_SUPPORTED_TOKENS_PER_RANK 8192 // Must match kernel template in hybridep_adapter.cu #define HT_OF_NUM_TOKENS_PER_CHUNK 64 #ifndef ENABLE_FAST_DEBUG #define NUM_CPU_TIMEOUT_SECS 100 #define NUM_TIMEOUT_CYCLES 200000000000ull // 200G cycles ~= 100s #else #define NUM_CPU_TIMEOUT_SECS 10 #define NUM_TIMEOUT_CYCLES 20000000000ull // 20G cycles ~= 10s #endif #define LOW_LATENCY_SEND_PHASE 1 #define LOW_LATENCY_RECV_PHASE 2 // NCCL GIN Configuration // Make CLion CUDA indexing work #ifdef __CLION_IDE__ #define __CUDA_ARCH__ 900 // NOLINT(*-reserved-identifier) #define __CUDACC_RDC__ // NOLINT(*-reserved-identifier) #endif // Remove Torch restrictions #ifdef __CUDA_NO_HALF_CONVERSIONS__ #undef __CUDA_NO_HALF_CONVERSIONS__ #endif #ifdef __CUDA_NO_HALF_OPERATORS__ #undef __CUDA_NO_HALF_OPERATORS__ #endif #ifdef __CUDA_NO_HALF2_OPERATORS__ #undef __CUDA_NO_HALF2_OPERATORS__ #endif #ifdef __CUDA_NO_BFLOAT16_CONVERSIONS__ #undef __CUDA_NO_BFLOAT16_CONVERSIONS__ #endif #ifdef __CUDA_NO_BFLOAT162_OPERATORS__ #undef __CUDA_NO_BFLOAT162_OPERATORS__ #endif // ============================================================================ // Standard includes // ============================================================================ #include #include #include #include #include #include #include #include "nccl.h" #include "nccl_device.h" #include "device/macros.cuh" namespace nccl_ep { // Internode low-latency kernels namespace internode_ll { void clean_low_latency_buffer(int* clean_0, int num_clean_int_0, int* clean_1, int num_clean_int_1, cudaStream_t stream); 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 use_fp8, bool roundScale, bool use_ue8m0, int phases, int numComms, ncclDevComm* devComms, const ncclWindow_t* windows, unsigned signalsBase, void* workspace, int num_device_sms, cudaStream_t stream); void combine(const void* inData, const int* srcInfo, const int64_t* layoutRange, const int64_t* inTopkIdx, const float* topkWeights, void* outData, void* sendBuf, void* recvBuf, int* recvFlagBuf, size_t sendOff, size_t recvOff, size_t recvFlagOff, int* nextRecvCntBuf, int nextRecvCntBufSize, int64_t* waitStats, int numCombinedTokens, int hidden, int maxTokensPerRank, int numTopk, int numExperts, int currRank, int numRanks, bool useLogFmt, int phases, bool zeroCopy, int numComms, ncclDevComm* devComms, const ncclWindow_t* windows, unsigned signalsBase, void* workspace, int num_device_sms, cudaStream_t stream); } // namespace internode_ll template __host__ __device__ constexpr dtype_t ceil_div(dtype_t a, dtype_t b) { return (a + b - 1) / b; } template __host__ __device__ constexpr dtype_t align(dtype_t a, dtype_t b) { return ceil_div(a, b) * b; } struct LowLatencyBuffer { int num_clean_int = 0; void* dispatch_rdma_send_buffer = nullptr; void* dispatch_rdma_recv_data_buffer = nullptr; int* dispatch_rdma_recv_count_buffer = nullptr; void* combine_rdma_send_buffer = nullptr; void* combine_rdma_recv_data_buffer = nullptr; int* combine_rdma_recv_flag_buffer = nullptr; void* combine_rdma_send_buffer_data_start = nullptr; size_t num_bytes_per_combine_msg = 0; std::pair clean_meta() { EP_HOST_ASSERT(dispatch_rdma_recv_count_buffer == combine_rdma_recv_flag_buffer); return {dispatch_rdma_recv_count_buffer, num_clean_int}; } }; struct LowLatencyLayout { size_t total_bytes = 0; LowLatencyBuffer buffers[2]; template out_ptr_t advance(const in_ptr_t& ptr, size_t count) { return reinterpret_cast(reinterpret_cast(ptr) + count); } LowLatencyLayout(void* rdma_buffer, int num_max_dispatch_tokens_per_rank, int hidden, int num_ranks, int num_experts) { const int num_scales = hidden / 128; // Dispatch and combine layout: // - 2 symmetric odd/even send buffer // - 2 symmetric odd/even receive buffers // - 2 symmetric odd/even signaling buffers // Message sizes // NOTES: you should add a control `int4` for combine messages if you want to do data transformation // NOTES: `num_scales * sizeof(nv_bfloat162)` means the per-128-channel min/max EP_HOST_ASSERT(num_scales * sizeof(float) <= hidden); size_t num_bytes_per_dispatch_msg = sizeof(int4) + std::max(hidden * sizeof(nv_bfloat16), hidden + num_scales * sizeof(float)); size_t num_bytes_per_combine_msg = num_scales * sizeof(nv_bfloat162) + hidden * sizeof(nv_bfloat16); // Send buffer size_t dispatch_send_buffer_bytes = num_max_dispatch_tokens_per_rank * num_bytes_per_dispatch_msg; size_t combine_send_buffer_bytes = num_experts * num_max_dispatch_tokens_per_rank * num_bytes_per_combine_msg; size_t send_buffer_bytes = std::max(dispatch_send_buffer_bytes, combine_send_buffer_bytes); EP_HOST_ASSERT(send_buffer_bytes % sizeof(int4) == 0); total_bytes += send_buffer_bytes * 2; // Symmetric receive buffers size_t dispatch_recv_data_buffer_bytes = num_experts * num_max_dispatch_tokens_per_rank * num_bytes_per_dispatch_msg; size_t combine_recv_buffer_bytes = num_experts * num_max_dispatch_tokens_per_rank * num_bytes_per_combine_msg; size_t recv_buffer_bytes = std::max(dispatch_recv_data_buffer_bytes, combine_recv_buffer_bytes); EP_HOST_ASSERT(recv_buffer_bytes % sizeof(int4) == 0); total_bytes += recv_buffer_bytes * 2; // Symmetric signaling buffers size_t dispatch_recv_count_buffer_bytes = num_experts * sizeof(int); size_t combine_recv_flag_buffer_bytes = dispatch_recv_count_buffer_bytes; size_t signaling_buffer_bytes = std::max(dispatch_recv_count_buffer_bytes, combine_recv_flag_buffer_bytes); size_t signaling_buffer_bytes_aligned = align(signaling_buffer_bytes, 128); total_bytes += signaling_buffer_bytes_aligned * 2; // Assign pointers for (int i = 0; i < 2; ++ i) { buffers[i] = { static_cast(signaling_buffer_bytes / sizeof(int)), advance(rdma_buffer, signaling_buffer_bytes_aligned * 2 + send_buffer_bytes * i), advance(rdma_buffer, signaling_buffer_bytes_aligned * 2 + send_buffer_bytes * 2 + recv_buffer_bytes * i), advance(rdma_buffer, signaling_buffer_bytes_aligned * i), advance(rdma_buffer, signaling_buffer_bytes_aligned * 2 + send_buffer_bytes * i), advance(rdma_buffer, signaling_buffer_bytes_aligned * 2 + send_buffer_bytes * 2 + recv_buffer_bytes * i), advance(rdma_buffer, signaling_buffer_bytes_aligned * i), advance(rdma_buffer, signaling_buffer_bytes_aligned * 2 + send_buffer_bytes * i), num_bytes_per_combine_msg }; } } }; inline unsigned long int get_low_latency_rdma_size_hint(int num_max_dispatch_tokens_per_rank, int hidden, int num_ranks, int num_experts) { auto num_bytes = LowLatencyLayout(nullptr, num_max_dispatch_tokens_per_rank, hidden, num_ranks, num_experts).total_bytes; return ((num_bytes + NUM_BUFFER_ALIGNMENT_BYTES) / NUM_BUFFER_ALIGNMENT_BYTES) * NUM_BUFFER_ALIGNMENT_BYTES; } } // namespace nccl_ep