// Copyright (c) 2025 PaddlePaddle Authors. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // The file has been adapted from DeepSeek DeepEP project // Copyright (c) 2025 DeepSeek // Licensed under the MIT License - // https://github.com/deepseek-ai/DeepEP/blob/main/LICENSE #include #include #include #include #include "paddle/fluid/distributed/collective/deep_ep/deep_ep.hpp" #include "paddle/fluid/distributed/collective/deep_ep/kernels/api.cuh" #include "paddle/fluid/distributed/collective/deep_ep/kernels/configs.cuh" #include "paddle/fluid/distributed/collective/deep_ep/include/CUDADataType.h" #include "paddle/fluid/distributed/collective/deep_ep/include/ScalarType.h" #include "paddle/fluid/distributed/collective/process_group_nccl.h" #include "paddle/phi/api/include/api.h" #include "paddle/phi/api/include/tensor_utils.h" #include "paddle/phi/common/data_type.h" #include "paddle/phi/common/place.h" #include "paddle/phi/core/distributed/utils.h" #include "paddle/phi/core/memory/allocation/allocator_facade.h" COMMON_DECLARE_int64(deep_ep_comm_prealloc_in_mb); namespace deep_ep { std::once_flag pre_alloc_once_flag; namespace detail { void SetAllocatorStreamForGPUContext(cudaStream_t stream, phi::GPUContext* ctx) { ctx->SetAllocator(paddle::memory::allocation::AllocatorFacade::Instance() .GetAllocator(ctx->GetPlace(), stream) .get()); } } // namespace detail void PreAlloc(paddle::Tensor tensor, cudaStream_t stream) { int64_t numel = tensor.numel(); auto alloc_size = FLAGS_deep_ep_comm_prealloc_in_mb * 1000000; std::cout << "alloc once here, size: " << alloc_size << " numel: " << numel << std::endl; std::cout << tensor.place() << "\t" << stream << std::endl; paddle::memory::allocation::AllocatorFacade::Instance() .GetAllocator(tensor.place(), stream) ->Allocate(alloc_size); } Buffer::Buffer(int rank, int num_ranks, int64_t num_nvl_bytes, int64_t num_rdma_bytes, bool low_latency_mode, int context_ring_id) : rank(rank), num_ranks(num_ranks), num_nvl_bytes(num_nvl_bytes), num_rdma_bytes(num_rdma_bytes), low_latency_mode(low_latency_mode) { CUDA_CHECK(cudaGetDevice(&device_id)); auto map = paddle::distributed::ProcessGroupMapFromGid::getInstance(); paddle::distributed::ProcessGroup* pg = map->get(context_ring_id); const auto& place = phi::GPUPlace(device_id); comm_ctx = reinterpret_cast(pg) ->GetOrCreateCommContext(place, phi::distributed::CommType::ALLTOALL); comm_stream = comm_ctx->GetStream(); calc_ctx = reinterpret_cast( reinterpret_cast(pg) ->GetDeviceContext(place, true)); // Task fifo memory int64_t fifo_bytes = sizeof(int) * NUM_MAX_FIFO_SLOTS; int64_t buffer_ptr_bytes = sizeof(void*) * NUM_MAX_NVL_PEERS; int64_t task_ptr_bytes = sizeof(int*) * NUM_MAX_NVL_PEERS; // Common checks EP_HOST_ASSERT( num_nvl_bytes % NUM_BUFFER_ALIGNMENT_BYTES == 0 && ((low_latency_mode || num_nvl_bytes <= std::numeric_limits::max()) || num_rdma_bytes == 0)); EP_HOST_ASSERT( num_rdma_bytes % NUM_BUFFER_ALIGNMENT_BYTES == 0 && (low_latency_mode || num_rdma_bytes <= std::numeric_limits::max())); EP_HOST_ASSERT(0 <= rank && rank < num_ranks && (num_ranks <= NUM_MAX_NVL_PEERS * NUM_MAX_RDMA_PEERS || low_latency_mode)); EP_HOST_ASSERT(num_ranks < NUM_MAX_NVL_PEERS || num_ranks % NUM_MAX_NVL_PEERS == 0); if (num_rdma_bytes > 0) EP_HOST_ASSERT(num_ranks > NUM_MAX_NVL_PEERS || low_latency_mode); // Get ranks // CUDA_CHECK(cudaGetDevice(&device_id)); rdma_rank = rank / NUM_MAX_NVL_PEERS, nvl_rank = rank % NUM_MAX_NVL_PEERS; num_rdma_ranks = std::max(1, num_ranks / NUM_MAX_NVL_PEERS), num_nvl_ranks = std::min(num_ranks, NUM_MAX_NVL_PEERS); // Get device info cudaDeviceProp device_prop = {}; CUDA_CHECK(cudaGetDeviceProperties(&device_prop, device_id)); if (num_nvl_bytes > 0) { // Local IPC: alloc local memory and set local IPC handle CUDA_CHECK(cudaMalloc( &buffer_ptrs[nvl_rank], num_nvl_bytes + fifo_bytes + buffer_ptr_bytes + task_ptr_bytes)); CUDA_CHECK( cudaIpcGetMemHandle(&ipc_handles[nvl_rank], buffer_ptrs[nvl_rank])); buffer_ptrs_gpu = reinterpret_cast( reinterpret_cast(buffer_ptrs[nvl_rank]) + num_nvl_bytes + fifo_bytes); // Set task fifo EP_HOST_ASSERT(NUM_MAX_FIFO_SLOTS % num_nvl_ranks == 0); task_fifo_ptrs[nvl_rank] = reinterpret_cast( reinterpret_cast(buffer_ptrs[nvl_rank]) + num_nvl_bytes); task_fifo_ptrs_gpu = reinterpret_cast( reinterpret_cast(buffer_ptrs[nvl_rank]) + num_nvl_bytes + fifo_bytes + buffer_ptr_bytes); // No need to synchronize, will do a full device sync during `sync` CUDA_CHECK(cudaMemsetAsync( buffer_ptrs[nvl_rank], 0, num_nvl_bytes + fifo_bytes + buffer_ptr_bytes + task_ptr_bytes, comm_stream)); } // Create 32 MiB workspace // Note(ZKK): here we allocate more(2 * M2N_NUM_WORKSPACE) to support M2N! // Later we will optimize here! CUDA_CHECK( cudaMalloc(&workspace, 2 * M2N_NUM_WORKSPACE * NUM_WORKSPACE_BYTES)); CUDA_CHECK(cudaMemsetAsync( workspace, 0, 2 * M2N_NUM_WORKSPACE * NUM_WORKSPACE_BYTES, comm_stream)); // MoE counter CUDA_CHECK( cudaMallocHost(&moe_recv_counter, sizeof(int64_t), cudaHostAllocMapped)); CUDA_CHECK(cudaHostGetDevicePointer( &moe_recv_counter_mapped, const_cast(moe_recv_counter), 0)); *moe_recv_counter = -1; // MoE expert-level counter CUDA_CHECK(cudaMallocHost(&moe_recv_expert_counter, sizeof(int) * NUM_MAX_LOCAL_EXPERTS, cudaHostAllocMapped)); CUDA_CHECK(cudaHostGetDevicePointer(&moe_recv_expert_counter_mapped, const_cast(moe_recv_expert_counter), 0)); for (int i = 0; i < NUM_MAX_LOCAL_EXPERTS; ++i) moe_recv_expert_counter[i] = -1; // MoE RDMA-level counter if (num_rdma_ranks > 0) { CUDA_CHECK(cudaMallocHost( &moe_recv_rdma_counter, sizeof(int), cudaHostAllocMapped)); CUDA_CHECK(cudaHostGetDevicePointer(&moe_recv_rdma_counter_mapped, const_cast(moe_recv_rdma_counter), 0)); *moe_recv_rdma_counter = -1; } } Buffer::~Buffer() noexcept(false) { // Synchronize CUDA_CHECK(cudaDeviceSynchronize()); printf("Buffer::~Buffer begin!!!\n"); if (num_nvl_bytes > 0) { // Barrier intranode::barrier( task_fifo_ptrs_gpu, head, nvl_rank, num_nvl_ranks, comm_stream); move_fifo_slots(); CUDA_CHECK(cudaDeviceSynchronize()); // Close remote IPC if (is_available()) { for (int i = 0; i < num_nvl_ranks; ++i) if (i != nvl_rank) CUDA_CHECK(cudaIpcCloseMemHandle(buffer_ptrs[i])); } // Free local buffer and error flag CUDA_CHECK(cudaFree(buffer_ptrs[nvl_rank])); } #ifdef PADDLE_WITH_NVSHMEM // Free NVSHMEM if (num_rdma_bytes > 0) { CUDA_CHECK(cudaDeviceSynchronize()); internode::barrier(); internode::free(rdma_buffer_ptr); internode::finalize(); } #endif // Free cuBLAS handle, workspace and MoE counter CUDA_CHECK(cudaFree(workspace)); CUDA_CHECK(cudaFreeHost(const_cast(moe_recv_counter))); // Free chunked mode staffs CUDA_CHECK(cudaFreeHost(const_cast(moe_recv_expert_counter))); } void Buffer::move_fifo_slots(int num_slots) { head = (head + num_ranks * num_slots) % NUM_MAX_FIFO_SLOTS; } bool Buffer::is_available() const { return available; } bool Buffer::is_internode_available() const { #ifdef PADDLE_WITH_NVSHMEM return is_available() && num_ranks > NUM_MAX_NVL_PEERS; #else return false; #endif } int Buffer::get_num_rdma_ranks() const { return num_rdma_ranks; } int Buffer::get_rdma_rank() const { return rdma_rank; } int Buffer::get_root_rdma_rank(bool global) const { return global ? nvl_rank : 0; } int Buffer::get_local_device_id() const { return device_id; } cudaStream_t Buffer::get_comm_stream() const { return comm_stream; } #ifndef PADDLE_NO_PYTHON pybind11::bytearray Buffer::get_local_ipc_handle() const { return {ipc_handles[nvl_rank].reserved, CUDA_IPC_HANDLE_SIZE}; } pybind11::bytearray Buffer::get_local_nvshmem_unique_id() const { #ifdef PADDLE_WITH_NVSHMEM EP_HOST_ASSERT(rdma_rank == 0 && "Only RDMA rank 0 can get NVSHMEM unique ID"); auto unique_id = internode::get_unique_id(); #else LOG(ERROR) << "NVSHMEM is not enabled. You can enable it by setting cmake " "option WITH_NVSHMEM=ON."; std::vector unique_id; #endif return {reinterpret_cast(unique_id.data()), unique_id.size()}; } void Buffer::sync( const std::vector& device_ids, const std::vector>& all_gathered_handles, const std::optional& root_unique_id_opt) { EP_HOST_ASSERT(!is_available()); // Sync IPC handles if (num_nvl_bytes > 0) { EP_HOST_ASSERT(num_ranks == static_cast(device_ids.size())); EP_HOST_ASSERT(device_ids.size() == all_gathered_handles.size()); for (int i = 0, offset = rdma_rank * num_nvl_ranks; i < num_nvl_ranks; ++i) { EP_HOST_ASSERT(all_gathered_handles[offset + i].has_value()); auto handle_str = std::string(all_gathered_handles[offset + i].value()); EP_HOST_ASSERT(handle_str.size() == CUDA_IPC_HANDLE_SIZE); if (offset + i != rank) { std::memcpy( ipc_handles[i].reserved, handle_str.c_str(), CUDA_IPC_HANDLE_SIZE); CUDA_CHECK(cudaIpcOpenMemHandle( &buffer_ptrs[i], ipc_handles[i], cudaIpcMemLazyEnablePeerAccess)); task_fifo_ptrs[i] = reinterpret_cast( reinterpret_cast(buffer_ptrs[i]) + num_nvl_bytes); } else { EP_HOST_ASSERT(std::memcmp(ipc_handles[i].reserved, handle_str.c_str(), CUDA_IPC_HANDLE_SIZE) == 0); } } // Copy all buffer and task pointers to GPU CUDA_CHECK(cudaMemcpy(buffer_ptrs_gpu, buffer_ptrs, sizeof(void*) * NUM_MAX_NVL_PEERS, cudaMemcpyHostToDevice)); CUDA_CHECK(cudaMemcpy(task_fifo_ptrs_gpu, task_fifo_ptrs, sizeof(int*) * NUM_MAX_NVL_PEERS, cudaMemcpyHostToDevice)); CUDA_CHECK(cudaDeviceSynchronize()); } #ifdef PADDLE_WITH_NVSHMEM // Sync NVSHMEM handles and allocate memory if (num_rdma_bytes > 0) { // Initialize NVSHMEM EP_HOST_ASSERT(root_unique_id_opt.has_value()); std::vector root_unique_id(root_unique_id_opt->size()); auto root_unique_id_str = root_unique_id_opt->cast(); std::memcpy(root_unique_id.data(), root_unique_id_str.c_str(), root_unique_id_opt->size()); auto nvshmem_rank = low_latency_mode ? rank : rdma_rank; auto num_nvshmem_ranks = low_latency_mode ? num_ranks : num_rdma_ranks; EP_HOST_ASSERT(nvshmem_rank == internode::init(root_unique_id, nvshmem_rank, num_nvshmem_ranks, low_latency_mode)); internode::barrier(); // Allocate rdma_buffer_ptr = internode::alloc(num_rdma_bytes, NUM_BUFFER_ALIGNMENT_BYTES); // Clean buffer (mainly for low-latency mode) CUDA_CHECK(cudaMemset(rdma_buffer_ptr, 0, num_rdma_bytes)); // Barrier internode::barrier(); CUDA_CHECK(cudaDeviceSynchronize()); } #endif // Ready to use available = true; } #endif std::tuple, deep_ep::detail::Tensor, deep_ep::detail::Tensor, std::optional> Buffer::get_dispatch_layout(const deep_ep::detail::Tensor& topk_idx, int num_experts, std::optional& previous_event, bool async, bool allocate_on_comm_stream) { EP_HOST_ASSERT(topk_idx.dim() == 2); EP_HOST_ASSERT(topk_idx.is_contiguous()); EP_HOST_ASSERT(num_experts > 0); // Allocate all tensors on comm stream if set // NOTES: do not allocate tensors upfront! auto compute_stream = calc_ctx->stream(); if (allocate_on_comm_stream) { EP_HOST_ASSERT(previous_event.has_value() && async); deep_ep::detail::SetAllocatorStreamForGPUContext(comm_stream, calc_ctx); } // Wait previous tasks to be finished if (previous_event.has_value()) { stream_wait(comm_stream, previous_event.value()); } else { stream_wait(comm_stream, compute_stream); } auto num_tokens = static_cast(topk_idx.size(0)), num_topk = static_cast(topk_idx.size(1)); auto num_tokens_per_rank = ConvertPaddleTensorToDetailTensor(paddle::experimental::empty( {num_ranks}, phi::DataType::INT32, phi::GPUPlace(device_id))); auto num_tokens_per_rdma_rank = std::optional(); auto num_tokens_per_expert = ConvertPaddleTensorToDetailTensor(paddle::experimental::empty( {num_experts}, phi::DataType::INT32, phi::GPUPlace(device_id))); auto is_token_in_rank = ConvertPaddleTensorToDetailTensor( paddle::experimental::empty({num_tokens, num_ranks}, phi::DataType::BOOL, phi::GPUPlace(device_id))); if (is_internode_available()) num_tokens_per_rdma_rank = ConvertPaddleTensorToDetailTensor(paddle::experimental::empty( {num_rdma_ranks}, phi::DataType::INT32, phi::GPUPlace(device_id))); // get_dispatch_layout is used for both intranode and internode. internode::get_dispatch_layout( topk_idx.data_ptr(), num_tokens_per_rank.data_ptr(), num_tokens_per_rdma_rank.has_value() ? num_tokens_per_rdma_rank.value().data_ptr() : nullptr, num_tokens_per_expert.data_ptr(), is_token_in_rank.data_ptr(), num_tokens, num_topk, num_ranks, num_experts, comm_stream); // Wait streams std::optional event; if (async) { event = EventHandle(comm_stream); for (auto& t : {topk_idx, num_tokens_per_rank, num_tokens_per_expert, is_token_in_rank}) { t.record_stream(comm_stream); if (allocate_on_comm_stream) t.record_stream(compute_stream); } for (auto& to : {num_tokens_per_rdma_rank}) { to.has_value() ? to->record_stream(comm_stream) : void(); if (allocate_on_comm_stream) to.has_value() ? to->record_stream(compute_stream) : void(); } } else { stream_wait(compute_stream, comm_stream); } // Switch back compute stream if (allocate_on_comm_stream) { deep_ep::detail::SetAllocatorStreamForGPUContext(compute_stream, calc_ctx); } return {num_tokens_per_rank, num_tokens_per_rdma_rank, num_tokens_per_expert, is_token_in_rank, event}; } std::tuple, std::optional, std::optional, std::vector, deep_ep::detail::Tensor, deep_ep::detail::Tensor, deep_ep::detail::Tensor, deep_ep::detail::Tensor, deep_ep::detail::Tensor, std::optional> Buffer::intranode_dispatch( const deep_ep::detail::Tensor& x, const std::optional& x_scales, const std::optional& topk_idx, const std::optional& topk_weights, const std::optional& num_tokens_per_rank, const deep_ep::detail::Tensor& is_token_in_rank, const std::optional& num_tokens_per_expert, int cached_num_recv_tokens, const std::optional& cached_rank_prefix_matrix, const std::optional& cached_channel_prefix_matrix, int expert_alignment, const Config& config, std::optional& previous_event, // NOLINT bool async, bool allocate_on_comm_stream) { bool cached_mode = cached_rank_prefix_matrix.has_value(); // One channel use two blocks, even-numbered blocks for sending, odd-numbered // blocks for receiving. EP_HOST_ASSERT(config.num_sms % 2 == 0); int num_channels = config.num_sms / 2; if (cached_mode) { EP_HOST_ASSERT(cached_rank_prefix_matrix.has_value()); EP_HOST_ASSERT(cached_channel_prefix_matrix.has_value()); } else { EP_HOST_ASSERT(num_tokens_per_rank.has_value()); EP_HOST_ASSERT(num_tokens_per_expert.has_value()); } // Type checks EP_HOST_ASSERT(is_token_in_rank.scalar_type() == deep_ep::detail::kBool); if (cached_mode) { EP_HOST_ASSERT(cached_rank_prefix_matrix->scalar_type() == deep_ep::detail::kInt32); EP_HOST_ASSERT(cached_channel_prefix_matrix->scalar_type() == deep_ep::detail::kInt32); } else { EP_HOST_ASSERT(num_tokens_per_expert->scalar_type() == deep_ep::detail::kInt32); EP_HOST_ASSERT(num_tokens_per_rank->scalar_type() == deep_ep::detail::kInt32); } // Shape and contiguous checks EP_HOST_ASSERT(x.dim() == 2 && x.is_contiguous()); EP_HOST_ASSERT((x.size(1) * x.element_size()) % sizeof(int4) == 0); EP_HOST_ASSERT(is_token_in_rank.dim() == 2 && is_token_in_rank.is_contiguous()); EP_HOST_ASSERT(is_token_in_rank.size(0) == x.size(0) && is_token_in_rank.size(1) == num_ranks); if (cached_mode) { EP_HOST_ASSERT(cached_rank_prefix_matrix->dim() == 2 && cached_rank_prefix_matrix->is_contiguous()); EP_HOST_ASSERT(cached_rank_prefix_matrix->size(0) == num_ranks && cached_rank_prefix_matrix->size(1) == num_ranks); EP_HOST_ASSERT(cached_channel_prefix_matrix->dim() == 2 && cached_channel_prefix_matrix->is_contiguous()); EP_HOST_ASSERT(cached_channel_prefix_matrix->size(0) == num_ranks && cached_channel_prefix_matrix->size(1) == num_channels); } else { EP_HOST_ASSERT(num_tokens_per_expert->dim() == 1 && num_tokens_per_expert->is_contiguous()); EP_HOST_ASSERT(num_tokens_per_expert->size(0) % num_ranks == 0); EP_HOST_ASSERT(num_tokens_per_expert->size(0) / num_ranks <= NUM_MAX_LOCAL_EXPERTS); EP_HOST_ASSERT(num_tokens_per_rank->dim() == 1 && num_tokens_per_rank->is_contiguous()); EP_HOST_ASSERT(num_tokens_per_rank->size(0) == num_ranks); } auto num_tokens = static_cast(x.size(0)), hidden = static_cast(x.size(1)); auto num_experts = cached_mode ? 0 : static_cast(num_tokens_per_expert->size(0)), num_local_experts = num_experts / num_ranks; // Top-k checks int num_topk = 0; int64_t* topk_idx_ptr = nullptr; float* topk_weights_ptr = nullptr; EP_HOST_ASSERT(topk_idx.has_value() == topk_weights.has_value()); if (topk_idx.has_value()) { num_topk = static_cast(topk_idx->size(1)); EP_HOST_ASSERT(num_experts > 0); EP_HOST_ASSERT(topk_idx->dim() == 2 && topk_idx->is_contiguous()); EP_HOST_ASSERT(topk_weights->dim() == 2 && topk_weights->is_contiguous()); EP_HOST_ASSERT(num_tokens == topk_idx->size(0) && num_tokens == topk_weights->size(0)); EP_HOST_ASSERT(num_topk == topk_weights->size(1)); EP_HOST_ASSERT(topk_weights->scalar_type() == deep_ep::detail::kFloat32); topk_idx_ptr = topk_idx->data_ptr(); topk_weights_ptr = topk_weights->data_ptr(); } // FP8 scales checks float* x_scales_ptr = nullptr; int num_scales = 0; if (x_scales.has_value()) { EP_HOST_ASSERT(x.element_size() == 1); EP_HOST_ASSERT(x_scales->scalar_type() == deep_ep::detail::kFloat32); EP_HOST_ASSERT(x_scales->dim() > 0 && x_scales->dim() < 3 && x_scales->is_contiguous()); EP_HOST_ASSERT(x_scales->size(0) == num_tokens); num_scales = x_scales->dim() == 1 ? 1 : static_cast(x_scales->size(1)); x_scales_ptr = x_scales->data_ptr(); } // Allocate all tensors on comm stream if set // NOTES: do not allocate tensors upfront! auto compute_stream = calc_ctx->stream(); if (allocate_on_comm_stream) { EP_HOST_ASSERT(previous_event.has_value() && async); deep_ep::detail::SetAllocatorStreamForGPUContext(comm_stream, calc_ctx); if (FLAGS_deep_ep_comm_prealloc_in_mb > 0) std::call_once( pre_alloc_once_flag, PreAlloc, x.raw_tensor(), comm_stream); } // Wait previous tasks to be finished if (previous_event.has_value()) { stream_wait(comm_stream, previous_event.value()); } else { stream_wait(comm_stream, compute_stream); } // Create handles (only return for non-cached mode) int num_recv_tokens = -1; auto rank_prefix_matrix = deep_ep::detail::Tensor(); auto channel_prefix_matrix = deep_ep::detail::Tensor(); std::vector num_recv_tokens_per_expert_list; // Barrier or send sizes // To clean: channel start/end offset, head and tail int num_memset_int = num_channels * num_ranks * 4; if (cached_mode) { num_recv_tokens = cached_num_recv_tokens; rank_prefix_matrix = cached_rank_prefix_matrix.value(); channel_prefix_matrix = cached_channel_prefix_matrix.value(); // Copy rank prefix matrix and clean flags intranode::cached_notify_dispatch(rank_prefix_matrix.data_ptr(), num_memset_int, buffer_ptrs_gpu, task_fifo_ptrs_gpu, head, rank, num_ranks, comm_stream); move_fifo_slots(2); } else { rank_prefix_matrix = ConvertPaddleTensorToDetailTensor( paddle::experimental::empty({num_ranks, num_ranks}, phi::DataType::INT32, phi::GPUPlace(device_id))); channel_prefix_matrix = ConvertPaddleTensorToDetailTensor( paddle::experimental::empty({num_ranks, num_channels}, phi::DataType::INT32, phi::GPUPlace(device_id))); // Send sizes // Meta information: // - Size prefix by ranks, shaped as `[num_ranks, num_ranks]` // - Size prefix by experts (not used later), shaped as `[num_ranks, // num_local_experts]` // NOTES: no more token dropping in this version *moe_recv_counter = -1; for (int i = 0; i < num_local_experts; ++i) moe_recv_expert_counter[i] = -1; EP_HOST_ASSERT(num_ranks * (num_ranks + num_local_experts) * static_cast(sizeof(int)) <= num_nvl_bytes); intranode::notify_dispatch(num_tokens_per_rank->data_ptr(), moe_recv_counter_mapped, num_ranks, num_tokens_per_expert->data_ptr(), moe_recv_expert_counter_mapped, num_experts, num_tokens, is_token_in_rank.data_ptr(), channel_prefix_matrix.data_ptr(), rank_prefix_matrix.data_ptr(), num_memset_int, expert_alignment, buffer_ptrs_gpu, task_fifo_ptrs_gpu, head, rank, comm_stream, num_channels); move_fifo_slots(3); // Synchronize total received tokens and tokens per expert auto start_time = std::chrono::high_resolution_clock::now(); while (true) { // Read total count num_recv_tokens = static_cast(*moe_recv_counter); // Read per-expert count bool ready = (num_recv_tokens >= 0); for (int i = 0; i < num_local_experts && ready; ++i) ready &= moe_recv_expert_counter[i] >= 0; if (ready) break; // Timeout check if (std::chrono::duration_cast( std::chrono::high_resolution_clock::now() - start_time) .count() > NUM_CPU_TIMEOUT_SECS) throw std::runtime_error("DeepEP error: CPU recv timeout"); } num_recv_tokens_per_expert_list = std::vector( moe_recv_expert_counter, moe_recv_expert_counter + num_local_experts); } // Allocate new tensors auto recv_x = ConvertPaddleTensorToDetailTensor(paddle::experimental::empty( {num_recv_tokens, hidden}, x.dtype(), x.place())); auto recv_src_idx = ConvertPaddleTensorToDetailTensor(paddle::experimental::empty( {num_recv_tokens}, phi::DataType::INT32, phi::GPUPlace(device_id))); auto recv_topk_idx = std::optional(), recv_topk_weights = std::optional(), recv_x_scales = std::optional(); auto recv_channel_prefix_matrix = ConvertPaddleTensorToDetailTensor( paddle::experimental::empty({num_ranks, num_channels}, phi::DataType::INT32, phi::GPUPlace(device_id))); auto send_head = ConvertPaddleTensorToDetailTensor( paddle::experimental::empty({num_tokens, num_ranks}, phi::DataType::INT32, phi::GPUPlace(device_id))); // Assign pointers int64_t* recv_topk_idx_ptr = nullptr; float* recv_topk_weights_ptr = nullptr; float* recv_x_scales_ptr = nullptr; if (topk_idx.has_value()) { recv_topk_idx = ConvertPaddleTensorToDetailTensor(paddle::experimental::empty( {num_recv_tokens, num_topk}, topk_idx->dtype(), topk_idx->place())); recv_topk_weights = ConvertPaddleTensorToDetailTensor( paddle::experimental::empty({num_recv_tokens, num_topk}, topk_weights->dtype(), topk_idx->place())); recv_topk_idx_ptr = recv_topk_idx->data_ptr(); recv_topk_weights_ptr = recv_topk_weights->data_ptr(); } if (x_scales.has_value()) { recv_x_scales = x_scales->dim() == 1 ? ConvertPaddleTensorToDetailTensor(paddle::experimental::empty( {num_recv_tokens}, x_scales->dtype(), x_scales->place())) : ConvertPaddleTensorToDetailTensor( paddle::experimental::empty({num_recv_tokens, num_scales}, x_scales->dtype(), x_scales->place())); recv_x_scales_ptr = recv_x_scales->data_ptr(); } // Dispatch EP_HOST_ASSERT( num_ranks * num_ranks * static_cast(sizeof(int)) + // prefix matrix num_channels * num_ranks * static_cast(sizeof(int)) + // Channel start offset num_channels * num_ranks * static_cast(sizeof(int)) + // Channel end offset num_channels * num_ranks * static_cast(sizeof(int)) * 2 + // Queue head and tail num_channels * num_ranks * config.num_max_nvl_chunked_recv_tokens * hidden * recv_x.element_size() + // Data buffer num_channels * num_ranks * config.num_max_nvl_chunked_recv_tokens * static_cast(sizeof(int)) + // Source index buffer num_channels * num_ranks * config.num_max_nvl_chunked_recv_tokens * num_topk * static_cast(sizeof(int64_t)) + // Top-k index buffer num_channels * num_ranks * config.num_max_nvl_chunked_recv_tokens * num_topk * static_cast(sizeof(float)) + // Top-k weight buffer num_channels * num_ranks * config.num_max_nvl_chunked_recv_tokens * static_cast(sizeof(float)) * num_scales // FP8 scale buffer <= num_nvl_bytes); intranode::dispatch( recv_x.data_ptr(), recv_x_scales_ptr, recv_src_idx.data_ptr(), recv_topk_idx_ptr, recv_topk_weights_ptr, recv_channel_prefix_matrix.data_ptr(), send_head.data_ptr(), x.data_ptr(), x_scales_ptr, topk_idx_ptr, topk_weights_ptr, is_token_in_rank.data_ptr(), channel_prefix_matrix.data_ptr(), num_tokens, static_cast(hidden * recv_x.element_size() / sizeof(int4)), num_topk, num_experts, num_scales, buffer_ptrs_gpu, rank, num_ranks, comm_stream, config.num_sms, config.num_max_nvl_chunked_send_tokens, config.num_max_nvl_chunked_recv_tokens); // Wait streams std::optional event; if (async) { event = EventHandle(comm_stream); for (auto& t : {x, is_token_in_rank, rank_prefix_matrix, channel_prefix_matrix, recv_x, recv_src_idx, recv_channel_prefix_matrix, send_head}) { t.record_stream(comm_stream); if (allocate_on_comm_stream) t.record_stream(compute_stream); } for (auto& to : {x_scales, topk_idx, topk_weights, num_tokens_per_rank, num_tokens_per_expert, cached_channel_prefix_matrix, cached_rank_prefix_matrix, recv_topk_idx, recv_topk_weights, recv_x_scales}) { to.has_value() ? to->record_stream(comm_stream) : void(); if (allocate_on_comm_stream) to.has_value() ? to->record_stream(compute_stream) : void(); } } else { stream_wait(compute_stream, comm_stream); } // Switch back compute stream if (allocate_on_comm_stream) { deep_ep::detail::SetAllocatorStreamForGPUContext(compute_stream, calc_ctx); } // Return values return {recv_x, recv_x_scales, recv_topk_idx, recv_topk_weights, num_recv_tokens_per_expert_list, rank_prefix_matrix, channel_prefix_matrix, recv_channel_prefix_matrix, recv_src_idx, send_head, event}; } std::tuple, std::optional> Buffer::intranode_combine( const deep_ep::detail::Tensor& x, const std::optional& topk_weights, const deep_ep::detail::Tensor& src_idx, const deep_ep::detail::Tensor& rank_prefix_matrix, const deep_ep::detail::Tensor& channel_prefix_matrix, const deep_ep::detail::Tensor& send_head, const Config& config, std::optional& previous_event, bool async, bool allocate_on_comm_stream) { EP_HOST_ASSERT(x.dim() == 2 && x.is_contiguous()); EP_HOST_ASSERT(src_idx.dim() == 1 && src_idx.is_contiguous() && src_idx.scalar_type() == deep_ep::detail::kInt32); EP_HOST_ASSERT(send_head.dim() == 2 && send_head.is_contiguous() && send_head.scalar_type() == deep_ep::detail::kInt32); EP_HOST_ASSERT(rank_prefix_matrix.dim() == 2 && rank_prefix_matrix.is_contiguous() && rank_prefix_matrix.scalar_type() == deep_ep::detail::kInt32); EP_HOST_ASSERT(channel_prefix_matrix.dim() == 2 && channel_prefix_matrix.is_contiguous() && channel_prefix_matrix.scalar_type() == deep_ep::detail::kInt32); // One channel use two blocks, even-numbered blocks for sending, odd-numbered // blocks for receiving. EP_HOST_ASSERT(config.num_sms % 2 == 0); int num_channels = config.num_sms / 2; auto num_tokens = static_cast(x.size(0)), hidden = static_cast(x.size(1)); auto num_recv_tokens = static_cast(send_head.size(0)); EP_HOST_ASSERT(src_idx.size(0) == num_tokens); EP_HOST_ASSERT(send_head.size(1) == num_ranks); EP_HOST_ASSERT(rank_prefix_matrix.size(0) == num_ranks && rank_prefix_matrix.size(1) == num_ranks); EP_HOST_ASSERT(channel_prefix_matrix.size(0) == num_ranks && channel_prefix_matrix.size(1) == num_channels); EP_HOST_ASSERT((hidden * x.element_size()) % sizeof(int4) == 0); // Allocate all tensors on comm stream if set // NOTES: do not allocate tensors upfront! auto compute_stream = calc_ctx->stream(); if (allocate_on_comm_stream) { EP_HOST_ASSERT(previous_event.has_value() && async); deep_ep::detail::SetAllocatorStreamForGPUContext(comm_stream, calc_ctx); } // Wait previous tasks to be finished if (previous_event.has_value()) { stream_wait(comm_stream, previous_event.value()); } else { stream_wait(comm_stream, compute_stream); } int num_topk = 0; auto recv_topk_weights = std::optional(); float* topk_weights_ptr = nullptr; float* recv_topk_weights_ptr = nullptr; if (topk_weights.has_value()) { EP_HOST_ASSERT(topk_weights->dim() == 2 && topk_weights->is_contiguous()); EP_HOST_ASSERT(topk_weights->size(0) == num_tokens); EP_HOST_ASSERT(topk_weights->scalar_type() == deep_ep::detail::kFloat32); num_topk = static_cast(topk_weights->size(1)); topk_weights_ptr = topk_weights->data_ptr(); recv_topk_weights = ConvertPaddleTensorToDetailTensor( paddle::experimental::empty({num_recv_tokens, num_topk}, topk_weights->dtype(), topk_weights->place())); recv_topk_weights_ptr = recv_topk_weights->data_ptr(); } // Launch barrier and reset queue head and tail EP_HOST_ASSERT(num_channels * num_ranks * static_cast(sizeof(int)) * 2 <= num_nvl_bytes); intranode::cached_notify_combine(buffer_ptrs_gpu, send_head.data_ptr(), num_channels, num_recv_tokens, num_channels * num_ranks * 2, task_fifo_ptrs_gpu, head, rank, num_ranks, comm_stream); // NOTES: this function uses two FIFO slots (barrier before and after) move_fifo_slots(2); // Combine data auto recv_x = ConvertPaddleTensorToDetailTensor(paddle::experimental::empty( {num_recv_tokens, hidden}, x.dtype(), x.place())); EP_HOST_ASSERT( num_channels * num_ranks * static_cast(sizeof(int)) * 2 + // Queue head and tail num_channels * num_ranks * config.num_max_nvl_chunked_recv_tokens * hidden * x.element_size() + // Data buffer num_channels * num_ranks * config.num_max_nvl_chunked_recv_tokens * static_cast(sizeof(int)) + // Source index buffer num_channels * num_ranks * config.num_max_nvl_chunked_recv_tokens * num_topk * static_cast(sizeof(float)) // Top-k weight buffer <= num_nvl_bytes); intranode::combine(deep_ep::detail::ScalarTypeToCudaDataType(x.scalar_type()), recv_x.data_ptr(), recv_topk_weights_ptr, x.data_ptr(), topk_weights_ptr, src_idx.data_ptr(), rank_prefix_matrix.data_ptr(), channel_prefix_matrix.data_ptr(), send_head.data_ptr(), num_tokens, num_recv_tokens, hidden, num_topk, buffer_ptrs_gpu, rank, num_ranks, comm_stream, config.num_sms, config.num_max_nvl_chunked_send_tokens, config.num_max_nvl_chunked_recv_tokens); // Wait streams std::optional event; if (async) { event = EventHandle(comm_stream); for (auto& t : {x, src_idx, send_head, rank_prefix_matrix, channel_prefix_matrix, recv_x}) { t.record_stream(comm_stream); if (allocate_on_comm_stream) t.record_stream(compute_stream); } for (auto& to : {topk_weights, recv_topk_weights}) { to.has_value() ? to->record_stream(comm_stream) : void(); if (allocate_on_comm_stream) to.has_value() ? to->record_stream(compute_stream) : void(); } } else { stream_wait(compute_stream, comm_stream); } // Switch back compute stream if (allocate_on_comm_stream) { deep_ep::detail::SetAllocatorStreamForGPUContext(compute_stream, calc_ctx); } return {recv_x, recv_topk_weights, event}; } #ifdef PADDLE_WITH_NVSHMEM std::tuple, std::optional, std::optional, std::vector, deep_ep::detail::Tensor, deep_ep::detail::Tensor, std::optional, deep_ep::detail::Tensor, std::optional, deep_ep::detail::Tensor, std::optional, std::optional, std::optional, std::optional> Buffer::internode_dispatch( const deep_ep::detail::Tensor& x, const std::optional& x_scales, const std::optional& topk_idx, const std::optional& topk_weights, const std::optional& num_tokens_per_rank, const std::optional& num_tokens_per_rdma_rank, const deep_ep::detail::Tensor& is_token_in_rank, const std::optional& num_tokens_per_expert, int cached_num_recv_tokens, int cached_num_rdma_recv_tokens, const std::optional& cached_rdma_channel_prefix_matrix, const std::optional& cached_recv_rdma_rank_prefix_sum, const std::optional& cached_gbl_channel_prefix_matrix, const std::optional& cached_recv_gbl_rank_prefix_sum, int expert_alignment, const Config& config, std::optional& previous_event, // NOLINT bool async, bool allocate_on_comm_stream) { // In dispatch, CPU will busy-wait until GPU receive tensor size metadata from // other ranks, which can be quite long. If users of DeepEP need to execute // other Python code on other threads, such as KV transfer, their code will // get stuck due to GIL unless we release GIL here. // pybind11::gil_scoped_release release; const int num_channels = config.num_sms / 2; EP_HOST_ASSERT(config.num_sms % 2 == 0); EP_HOST_ASSERT(0 < get_num_rdma_ranks() && get_num_rdma_ranks() <= NUM_MAX_RDMA_PEERS); bool cached_mode = cached_rdma_channel_prefix_matrix.has_value(); if (cached_mode) { EP_HOST_ASSERT(cached_rdma_channel_prefix_matrix.has_value()); EP_HOST_ASSERT(cached_recv_rdma_rank_prefix_sum.has_value()); EP_HOST_ASSERT(cached_gbl_channel_prefix_matrix.has_value()); EP_HOST_ASSERT(cached_recv_gbl_rank_prefix_sum.has_value()); } else { EP_HOST_ASSERT(num_tokens_per_rank.has_value()); EP_HOST_ASSERT(num_tokens_per_rdma_rank.has_value()); EP_HOST_ASSERT(num_tokens_per_expert.has_value()); } // Type checks if (cached_mode) { EP_HOST_ASSERT(cached_rdma_channel_prefix_matrix->scalar_type() == deep_ep::detail::kInt32); EP_HOST_ASSERT(cached_recv_rdma_rank_prefix_sum->scalar_type() == deep_ep::detail::kInt32); EP_HOST_ASSERT(cached_gbl_channel_prefix_matrix->scalar_type() == deep_ep::detail::kInt32); EP_HOST_ASSERT(cached_recv_gbl_rank_prefix_sum->scalar_type() == deep_ep::detail::kInt32); } else { EP_HOST_ASSERT(num_tokens_per_rank->scalar_type() == deep_ep::detail::kInt32); EP_HOST_ASSERT(num_tokens_per_rdma_rank->scalar_type() == deep_ep::detail::kInt32); EP_HOST_ASSERT(num_tokens_per_expert->scalar_type() == deep_ep::detail::kInt32); } // Shape and contiguous checks EP_HOST_ASSERT(x.dim() == 2 && x.is_contiguous()); EP_HOST_ASSERT((x.size(1) * x.element_size()) % sizeof(int4) == 0); if (cached_mode) { EP_HOST_ASSERT(cached_rdma_channel_prefix_matrix->dim() == 2 && cached_rdma_channel_prefix_matrix->is_contiguous()); EP_HOST_ASSERT(cached_rdma_channel_prefix_matrix->size(0) == num_rdma_ranks && cached_rdma_channel_prefix_matrix->size(1) == num_channels); EP_HOST_ASSERT(cached_recv_rdma_rank_prefix_sum->dim() == 1 && cached_recv_rdma_rank_prefix_sum->is_contiguous()); EP_HOST_ASSERT(cached_recv_rdma_rank_prefix_sum->size(0) == num_rdma_ranks); EP_HOST_ASSERT(cached_gbl_channel_prefix_matrix->dim() == 2 && cached_gbl_channel_prefix_matrix->is_contiguous()); EP_HOST_ASSERT(cached_gbl_channel_prefix_matrix->size(0) == num_ranks && cached_gbl_channel_prefix_matrix->size(1) == num_channels); EP_HOST_ASSERT(cached_recv_gbl_rank_prefix_sum->dim() == 1 && cached_recv_gbl_rank_prefix_sum->is_contiguous()); EP_HOST_ASSERT(cached_recv_gbl_rank_prefix_sum->size(0) == num_ranks); } else { EP_HOST_ASSERT(num_tokens_per_rank->dim() == 1 && num_tokens_per_rank->is_contiguous()); EP_HOST_ASSERT(num_tokens_per_rdma_rank->dim() == 1 && num_tokens_per_rdma_rank->is_contiguous()); EP_HOST_ASSERT(num_tokens_per_expert->dim() == 1 && num_tokens_per_expert->is_contiguous()); EP_HOST_ASSERT(num_tokens_per_rank->size(0) == num_ranks); EP_HOST_ASSERT(num_tokens_per_rdma_rank->size(0) == num_rdma_ranks); EP_HOST_ASSERT(num_tokens_per_expert->size(0) % num_ranks == 0); EP_HOST_ASSERT(num_tokens_per_expert->size(0) / num_ranks <= NUM_MAX_LOCAL_EXPERTS); } auto num_tokens = static_cast(x.size(0)), hidden = static_cast(x.size(1)), hidden_int4 = static_cast(x.size(1) * x.element_size() / sizeof(int4)); auto num_experts = cached_mode ? 0 : static_cast(num_tokens_per_expert->size(0)), num_local_experts = num_experts / num_ranks; // Top-k checks int num_topk = 0; int64_t* topk_idx_ptr = nullptr; float* topk_weights_ptr = nullptr; EP_HOST_ASSERT(topk_idx.has_value() == topk_weights.has_value()); if (topk_idx.has_value()) { num_topk = static_cast(topk_idx->size(1)); EP_HOST_ASSERT(num_experts > 0); EP_HOST_ASSERT(topk_idx->dim() == 2 && topk_idx->is_contiguous()); EP_HOST_ASSERT(topk_weights->dim() == 2 && topk_weights->is_contiguous()); EP_HOST_ASSERT(num_tokens == topk_idx->size(0) && num_tokens == topk_weights->size(0)); EP_HOST_ASSERT(num_topk == topk_weights->size(1)); EP_HOST_ASSERT(topk_weights->scalar_type() == deep_ep::detail::kFloat32); topk_idx_ptr = topk_idx->data_ptr(); topk_weights_ptr = topk_weights->data_ptr(); } // FP8 scales checks float* x_scales_ptr = nullptr; int num_scales = 0; if (x_scales.has_value()) { EP_HOST_ASSERT(x.element_size() == 1); EP_HOST_ASSERT(x_scales->scalar_type() == deep_ep::detail::kFloat32); EP_HOST_ASSERT(x_scales->dim() > 0 && x_scales->dim() < 3 && x_scales->is_contiguous()); EP_HOST_ASSERT(x_scales->size(0) == num_tokens); num_scales = x_scales->dim() == 1 ? 1 : static_cast(x_scales->size(1)); x_scales_ptr = x_scales->data_ptr(); } // Allocate all tensors on comm stream if set // NOTES: do not allocate tensors upfront! auto compute_stream = calc_ctx->stream(); if (allocate_on_comm_stream) { EP_HOST_ASSERT(previous_event.has_value() && async); deep_ep::detail::SetAllocatorStreamForGPUContext(comm_stream, calc_ctx); if (FLAGS_deep_ep_comm_prealloc_in_mb > 0) std::call_once( pre_alloc_once_flag, PreAlloc, x.raw_tensor(), comm_stream); } // Wait previous tasks to be finished if (previous_event.has_value()) { stream_wait(comm_stream, previous_event.value()); } else { stream_wait(comm_stream, compute_stream); } // Create handles (only return for non-cached mode) int num_recv_tokens = -1, num_rdma_recv_tokens = -1; auto rdma_channel_prefix_matrix = deep_ep::detail::Tensor(); auto recv_rdma_rank_prefix_sum = deep_ep::detail::Tensor(); auto gbl_channel_prefix_matrix = deep_ep::detail::Tensor(); auto recv_gbl_rank_prefix_sum = deep_ep::detail::Tensor(); std::vector num_recv_tokens_per_expert_list; // Barrier or send sizes if (cached_mode) { num_recv_tokens = cached_num_recv_tokens; num_rdma_recv_tokens = cached_num_rdma_recv_tokens; rdma_channel_prefix_matrix = cached_rdma_channel_prefix_matrix.value(); recv_rdma_rank_prefix_sum = cached_recv_rdma_rank_prefix_sum.value(); gbl_channel_prefix_matrix = cached_gbl_channel_prefix_matrix.value(); recv_gbl_rank_prefix_sum = cached_recv_gbl_rank_prefix_sum.value(); // Just a barrier and clean flags internode::cached_notify( hidden_int4, num_scales, num_topk, num_topk, num_ranks, num_channels, 0, nullptr, nullptr, nullptr, nullptr, rdma_buffer_ptr, config.num_max_rdma_chunked_recv_tokens, buffer_ptrs_gpu, config.num_max_nvl_chunked_recv_tokens, task_fifo_ptrs_gpu, head, rank, comm_stream, config.get_rdma_buffer_size_hint(hidden_int4 * sizeof(int4), num_ranks), num_nvl_bytes, true, low_latency_mode); move_fifo_slots(2); } else { rdma_channel_prefix_matrix = ConvertPaddleTensorToDetailTensor( paddle::experimental::empty({num_rdma_ranks, num_channels}, phi::DataType::INT32, phi::GPUPlace(device_id))); recv_rdma_rank_prefix_sum = ConvertPaddleTensorToDetailTensor(paddle::experimental::empty( {num_rdma_ranks}, phi::DataType::INT32, phi::GPUPlace(device_id))); gbl_channel_prefix_matrix = ConvertPaddleTensorToDetailTensor( paddle::experimental::empty({num_ranks, num_channels}, phi::DataType::INT32, phi::GPUPlace(device_id))); recv_gbl_rank_prefix_sum = ConvertPaddleTensorToDetailTensor(paddle::experimental::empty( {num_ranks}, phi::DataType::INT32, phi::GPUPlace(device_id))); // Send sizes *moe_recv_counter = -1, *moe_recv_rdma_counter = -1; for (int i = 0; i < num_local_experts; ++i) moe_recv_expert_counter[i] = -1; internode::notify_dispatch( num_tokens_per_rank->data_ptr(), moe_recv_counter_mapped, num_ranks, num_tokens_per_rdma_rank->data_ptr(), moe_recv_rdma_counter_mapped, num_tokens_per_expert->data_ptr(), moe_recv_expert_counter_mapped, num_experts, is_token_in_rank.data_ptr(), num_tokens, num_channels, hidden_int4, num_scales, num_topk, expert_alignment, rdma_channel_prefix_matrix.data_ptr(), recv_rdma_rank_prefix_sum.data_ptr(), gbl_channel_prefix_matrix.data_ptr(), recv_gbl_rank_prefix_sum.data_ptr(), rdma_buffer_ptr, config.num_max_rdma_chunked_recv_tokens, buffer_ptrs_gpu, config.num_max_nvl_chunked_recv_tokens, task_fifo_ptrs_gpu, head, rank, comm_stream, config.get_rdma_buffer_size_hint(hidden_int4 * sizeof(int4), num_ranks), num_nvl_bytes, low_latency_mode); move_fifo_slots(3); // Synchronize total received tokens and tokens per expert auto start_time = std::chrono::high_resolution_clock::now(); while (true) { // Read total count num_recv_tokens = static_cast(*moe_recv_counter); num_rdma_recv_tokens = static_cast(*moe_recv_rdma_counter); // Read per-expert count bool ready = (num_recv_tokens >= 0) && (num_rdma_recv_tokens >= 0); for (int i = 0; i < num_local_experts && ready; ++i) ready &= moe_recv_expert_counter[i] >= 0; if (ready) break; // Timeout check if (std::chrono::duration_cast( std::chrono::high_resolution_clock::now() - start_time) .count() > NUM_CPU_TIMEOUT_SECS) { LOG(INFO) << "Global rank: " << rank << ", num_recv_tokens: " << num_recv_tokens << ", num_rdma_recv_tokens: " << num_rdma_recv_tokens; for (int i = 0; i < num_local_experts; ++i) LOG(INFO) << "moe_recv_expert_counter[" << i << "]: " << moe_recv_expert_counter[i]; throw std::runtime_error("DeepEP error: timeout (dispatch CPU)"); } } num_recv_tokens_per_expert_list = std::vector( moe_recv_expert_counter, moe_recv_expert_counter + num_local_experts); } // Allocate new tensors auto recv_x = ConvertPaddleTensorToDetailTensor(paddle::experimental::empty( {num_recv_tokens, hidden}, x.dtype(), x.place())); auto recv_topk_idx = std::optional(), recv_topk_weights = std::optional(), recv_x_scales = std::optional(); auto recv_src_meta = std::optional(); auto recv_rdma_channel_prefix_matrix = std::optional(); auto recv_gbl_channel_prefix_matrix = std::optional(); auto send_rdma_head = std::optional(); auto send_nvl_head = std::optional(); if (!cached_mode) { recv_src_meta = ConvertPaddleTensorToDetailTensor(paddle::experimental::empty( {num_recv_tokens, internode::get_source_meta_bytes()}, phi::DataType::INT8, phi::GPUPlace(device_id))); recv_rdma_channel_prefix_matrix = ConvertPaddleTensorToDetailTensor( paddle::experimental::empty({num_rdma_ranks, num_channels}, phi::DataType::INT32, phi::GPUPlace(device_id))); recv_gbl_channel_prefix_matrix = ConvertPaddleTensorToDetailTensor( paddle::experimental::empty({num_ranks, num_channels}, phi::DataType::INT32, phi::GPUPlace(device_id))); send_rdma_head = ConvertPaddleTensorToDetailTensor( paddle::experimental::empty({num_tokens, num_rdma_ranks}, phi::DataType::INT32, phi::GPUPlace(device_id))); send_nvl_head = ConvertPaddleTensorToDetailTensor( paddle::experimental::empty({num_rdma_recv_tokens, NUM_MAX_NVL_PEERS}, phi::DataType::INT32, phi::GPUPlace(device_id))); } // Assign pointers int64_t* recv_topk_idx_ptr = nullptr; float* recv_topk_weights_ptr = nullptr; float* recv_x_scales_ptr = nullptr; if (topk_idx.has_value()) { recv_topk_idx = ConvertPaddleTensorToDetailTensor(paddle::experimental::empty( {num_recv_tokens, num_topk}, topk_idx->dtype(), topk_idx->place())); recv_topk_weights = ConvertPaddleTensorToDetailTensor( paddle::experimental::empty({num_recv_tokens, num_topk}, topk_weights->dtype(), topk_weights->place())); recv_topk_idx_ptr = recv_topk_idx->data_ptr(); recv_topk_weights_ptr = recv_topk_weights->data_ptr(); } if (x_scales.has_value()) { recv_x_scales = x_scales->dim() == 1 ? ConvertPaddleTensorToDetailTensor(paddle::experimental::empty( {num_recv_tokens}, x_scales->dtype(), x_scales->place())) : ConvertPaddleTensorToDetailTensor( paddle::experimental::empty({num_recv_tokens, num_scales}, x_scales->dtype(), x_scales->place())); recv_x_scales_ptr = recv_x_scales->data_ptr(); } // Launch data dispatch // NOTES: the buffer size checks are moved into the `.cu` file internode::dispatch( recv_x.data_ptr(), recv_x_scales_ptr, recv_topk_idx_ptr, recv_topk_weights_ptr, cached_mode ? nullptr : recv_src_meta->data_ptr(), x.data_ptr(), x_scales_ptr, topk_idx_ptr, topk_weights_ptr, cached_mode ? nullptr : send_rdma_head->data_ptr(), cached_mode ? nullptr : send_nvl_head->data_ptr(), cached_mode ? nullptr : recv_rdma_channel_prefix_matrix->data_ptr(), cached_mode ? nullptr : recv_gbl_channel_prefix_matrix->data_ptr(), rdma_channel_prefix_matrix.data_ptr(), recv_rdma_rank_prefix_sum.data_ptr(), gbl_channel_prefix_matrix.data_ptr(), recv_gbl_rank_prefix_sum.data_ptr(), num_tokens, hidden_int4, num_scales, num_topk, num_experts, is_token_in_rank.data_ptr(), rdma_buffer_ptr, config.num_max_rdma_chunked_send_tokens, config.num_max_rdma_chunked_recv_tokens, buffer_ptrs_gpu, config.num_max_nvl_chunked_send_tokens, config.num_max_nvl_chunked_recv_tokens, rank, num_ranks, cached_mode, comm_stream, num_channels, low_latency_mode); // Wait streams std::optional event; if (async) { event = EventHandle(comm_stream); for (auto& t : {x, is_token_in_rank, recv_x, rdma_channel_prefix_matrix, recv_rdma_rank_prefix_sum, gbl_channel_prefix_matrix, recv_gbl_rank_prefix_sum}) { t.record_stream(comm_stream); if (allocate_on_comm_stream) t.record_stream(compute_stream); } for (auto& to : {x_scales, topk_idx, topk_weights, num_tokens_per_rank, num_tokens_per_rdma_rank, num_tokens_per_expert, cached_rdma_channel_prefix_matrix, cached_recv_rdma_rank_prefix_sum, cached_gbl_channel_prefix_matrix, cached_recv_gbl_rank_prefix_sum, recv_topk_idx, recv_topk_weights, recv_x_scales, recv_rdma_channel_prefix_matrix, recv_gbl_channel_prefix_matrix, send_rdma_head, send_nvl_head, recv_src_meta}) { to.has_value() ? to->record_stream(comm_stream) : void(); if (allocate_on_comm_stream) to.has_value() ? to->record_stream(compute_stream) : void(); } } else { stream_wait(compute_stream, comm_stream); } // Switch back compute stream if (allocate_on_comm_stream) { deep_ep::detail::SetAllocatorStreamForGPUContext(compute_stream, calc_ctx); } // Return values return {recv_x, recv_x_scales, recv_topk_idx, recv_topk_weights, num_recv_tokens_per_expert_list, rdma_channel_prefix_matrix, gbl_channel_prefix_matrix, recv_rdma_channel_prefix_matrix, recv_rdma_rank_prefix_sum, recv_gbl_channel_prefix_matrix, recv_gbl_rank_prefix_sum, recv_src_meta, send_rdma_head, send_nvl_head, event}; } std::tuple, std::optional> Buffer::internode_combine( const deep_ep::detail::Tensor& x, const std::optional& topk_weights, const deep_ep::detail::Tensor& src_meta, const deep_ep::detail::Tensor& is_combined_token_in_rank, const deep_ep::detail::Tensor& rdma_channel_prefix_matrix, const deep_ep::detail::Tensor& rdma_rank_prefix_sum, const deep_ep::detail::Tensor& gbl_channel_prefix_matrix, const deep_ep::detail::Tensor& combined_rdma_head, const deep_ep::detail::Tensor& combined_nvl_head, const Config& config, std::optional& previous_event, // NOLINT bool async, bool allocate_on_comm_stream) { const int num_channels = config.num_sms / 2; EP_HOST_ASSERT(config.num_sms % 2 == 0); // Shape and contiguous checks EP_HOST_ASSERT(x.dim() == 2 && x.is_contiguous()); EP_HOST_ASSERT(src_meta.dim() == 2 && src_meta.is_contiguous() && src_meta.scalar_type() == deep_ep::detail::kByte); EP_HOST_ASSERT(is_combined_token_in_rank.dim() == 2 && is_combined_token_in_rank.is_contiguous() && is_combined_token_in_rank.scalar_type() == deep_ep::detail::kBool); EP_HOST_ASSERT(rdma_channel_prefix_matrix.dim() == 2 && rdma_channel_prefix_matrix.is_contiguous() && rdma_channel_prefix_matrix.scalar_type() == deep_ep::detail::kInt32); EP_HOST_ASSERT(rdma_rank_prefix_sum.dim() == 1 && rdma_rank_prefix_sum.is_contiguous() && rdma_rank_prefix_sum.scalar_type() == deep_ep::detail::kInt32); EP_HOST_ASSERT(gbl_channel_prefix_matrix.dim() == 2 && gbl_channel_prefix_matrix.is_contiguous() && gbl_channel_prefix_matrix.scalar_type() == deep_ep::detail::kInt32); EP_HOST_ASSERT(combined_rdma_head.dim() == 2 && combined_rdma_head.is_contiguous() && combined_rdma_head.scalar_type() == deep_ep::detail::kInt32); EP_HOST_ASSERT(combined_nvl_head.dim() == 2 && combined_nvl_head.is_contiguous() && combined_nvl_head.scalar_type() == deep_ep::detail::kInt32); auto num_tokens = static_cast(x.size(0)), hidden = static_cast(x.size(1)), hidden_int4 = static_cast(x.size(1) * x.element_size() / sizeof(int4)); auto num_combined_tokens = static_cast(is_combined_token_in_rank.size(0)); EP_HOST_ASSERT((hidden * x.element_size()) % sizeof(int4) == 0); EP_HOST_ASSERT(src_meta.size(1) == internode::get_source_meta_bytes()); EP_HOST_ASSERT(is_combined_token_in_rank.size(1) == num_ranks); EP_HOST_ASSERT(rdma_channel_prefix_matrix.size(0) == num_rdma_ranks && rdma_channel_prefix_matrix.size(1) == num_channels); EP_HOST_ASSERT(rdma_rank_prefix_sum.size(0) == num_rdma_ranks); EP_HOST_ASSERT(gbl_channel_prefix_matrix.size(0) == num_ranks && gbl_channel_prefix_matrix.size(1) == num_channels); EP_HOST_ASSERT(combined_rdma_head.dim() == 2 && combined_rdma_head.size(0) == num_combined_tokens && combined_rdma_head.size(1) == num_rdma_ranks); EP_HOST_ASSERT(combined_nvl_head.dim() == 2 && combined_nvl_head.size(1) == NUM_MAX_NVL_PEERS); // Allocate all tensors on comm stream if set // NOTES: do not allocate tensors upfront! auto compute_stream = calc_ctx->stream(); if (allocate_on_comm_stream) { EP_HOST_ASSERT(previous_event.has_value() && async); deep_ep::detail::SetAllocatorStreamForGPUContext(comm_stream, calc_ctx); } // Wait previous tasks to be finished if (previous_event.has_value()) { stream_wait(comm_stream, previous_event.value()); } else { stream_wait(comm_stream, compute_stream); } // Top-k checks int num_topk = 0; auto combined_topk_weights = std::optional(); float* topk_weights_ptr = nullptr; float* combined_topk_weights_ptr = nullptr; if (topk_weights.has_value()) { EP_HOST_ASSERT(topk_weights->dim() == 2 && topk_weights->is_contiguous()); EP_HOST_ASSERT(topk_weights->size(0) == num_tokens); EP_HOST_ASSERT(topk_weights->scalar_type() == deep_ep::detail::kFloat32); num_topk = static_cast(topk_weights->size(1)); topk_weights_ptr = topk_weights->data_ptr(); combined_topk_weights = ConvertPaddleTensorToDetailTensor( paddle::experimental::empty({num_combined_tokens, num_topk}, topk_weights->dtype(), topk_weights->place())); combined_topk_weights_ptr = combined_topk_weights->data_ptr(); } // Extra check for avoid-dead-lock design EP_HOST_ASSERT(config.num_max_nvl_chunked_recv_tokens % num_rdma_ranks == 0); EP_HOST_ASSERT(config.num_max_nvl_chunked_send_tokens <= config.num_max_nvl_chunked_recv_tokens / num_rdma_ranks); // Launch barrier and reset queue head and tail internode::cached_notify( hidden_int4, 0, 0, num_topk, num_ranks, num_channels, num_combined_tokens, combined_rdma_head.data_ptr(), rdma_channel_prefix_matrix.data_ptr(), rdma_rank_prefix_sum.data_ptr(), combined_nvl_head.data_ptr(), rdma_buffer_ptr, config.num_max_rdma_chunked_recv_tokens, buffer_ptrs_gpu, config.num_max_nvl_chunked_recv_tokens, task_fifo_ptrs_gpu, head, rank, comm_stream, config.get_rdma_buffer_size_hint(hidden_int4 * sizeof(int4), num_ranks), num_nvl_bytes, false, low_latency_mode); move_fifo_slots(2); // Launch data combine auto combined_x = ConvertPaddleTensorToDetailTensor(paddle::experimental::empty( {num_combined_tokens, hidden}, x.dtype(), x.place())); internode::combine(deep_ep::detail::ScalarTypeToCudaDataType(x.scalar_type()), combined_x.data_ptr(), combined_topk_weights_ptr, is_combined_token_in_rank.data_ptr(), x.data_ptr(), topk_weights_ptr, combined_rdma_head.data_ptr(), combined_nvl_head.data_ptr(), src_meta.data_ptr(), rdma_channel_prefix_matrix.data_ptr(), rdma_rank_prefix_sum.data_ptr(), gbl_channel_prefix_matrix.data_ptr(), num_tokens, num_combined_tokens, hidden, num_topk, rdma_buffer_ptr, config.num_max_rdma_chunked_send_tokens, config.num_max_rdma_chunked_recv_tokens, buffer_ptrs_gpu, config.num_max_nvl_chunked_send_tokens, config.num_max_nvl_chunked_recv_tokens, rank, num_ranks, comm_stream, num_channels, low_latency_mode); // Wait streams std::optional event; if (async) { event = EventHandle(comm_stream); for (auto& t : {x, src_meta, is_combined_token_in_rank, rdma_channel_prefix_matrix, rdma_rank_prefix_sum, gbl_channel_prefix_matrix, combined_x, combined_rdma_head, combined_nvl_head}) { t.record_stream(comm_stream); if (allocate_on_comm_stream) t.record_stream(compute_stream); } for (auto& to : {topk_weights, combined_topk_weights}) { to.has_value() ? to->record_stream(comm_stream) : void(); if (allocate_on_comm_stream) to.has_value() ? to->record_stream(compute_stream) : void(); } } else { stream_wait(compute_stream, comm_stream); } // Switch back compute stream if (allocate_on_comm_stream) { deep_ep::detail::SetAllocatorStreamForGPUContext(compute_stream, calc_ctx); } // Return values return {combined_x, combined_topk_weights, event}; } #endif // PADDLE_WITH_NVSHMEM void Buffer::clean_low_latency_buffer(int num_max_dispatch_tokens_per_rank, int hidden, int num_experts) { #ifdef PADDLE_WITH_NVSHMEM EP_HOST_ASSERT(low_latency_mode); auto layout = LowLatencyLayout(rdma_buffer_ptr, num_max_dispatch_tokens_per_rank, hidden, num_ranks, num_experts); auto clean_meta_0 = layout.buffers[0].clean_meta(); auto clean_meta_1 = layout.buffers[1].clean_meta(); auto check_boundary = [=](void* ptr, size_t num_bytes) { auto offset = reinterpret_cast(ptr) - reinterpret_cast(rdma_buffer_ptr); EP_HOST_ASSERT(0 <= offset && offset + static_cast(num_bytes) <= num_rdma_bytes); }; check_boundary(clean_meta_0.first, clean_meta_0.second * sizeof(int)); check_boundary(clean_meta_1.first, clean_meta_1.second * sizeof(int)); internode_ll::clean_low_latency_buffer(clean_meta_0.first, clean_meta_0.second, clean_meta_1.first, clean_meta_1.second, calc_ctx->stream()); #else LOG(ERROR) << "NVSHMEM is not enabled. You can enable it by setting cmake " "option WITH_NVSHMEM=ON."; #endif } void Buffer::clean_low_latency_two_stage_buffer( int num_max_dispatch_tokens_per_rank, int hidden, int num_experts, int num_topk, int num_ranks, bool use_fp8) { #ifdef PADDLE_WITH_NVSHMEM EP_HOST_ASSERT(low_latency_mode); const int num_local_experts = num_experts / num_ranks; const int num_rdma_experts = num_local_experts * NUM_MAX_NVL_PEERS; const int num_scales = hidden / 128; const int num_rdma_ranks = num_ranks / NUM_MAX_NVL_PEERS; const size_t dispatch_num_bytes_per_msg = sizeof(int4) + (use_fp8 ? (hidden + num_scales * sizeof(float)) : (hidden * sizeof(nv_bfloat16))); auto dispatch_nvl_num_bytes = num_local_experts * num_ranks * num_max_dispatch_tokens_per_rank * dispatch_num_bytes_per_msg; const size_t combine_num_bytes_per_msg = hidden * sizeof(nv_bfloat16); auto combine_nvl_num_bytes = num_rdma_experts * num_rdma_ranks * num_max_dispatch_tokens_per_rank * combine_num_bytes_per_msg; const size_t signal_bytes = (num_local_experts * num_ranks * sizeof(int) + NUM_BUFFER_ALIGNMENT_BYTES - 1) / NUM_BUFFER_ALIGNMENT_BYTES * NUM_BUFFER_ALIGNMENT_BYTES; auto max_nvl_num_bytes = (std::max(dispatch_nvl_num_bytes, combine_nvl_num_bytes) + NUM_BUFFER_ALIGNMENT_BYTES - 1) / NUM_BUFFER_ALIGNMENT_BYTES * NUM_BUFFER_ALIGNMENT_BYTES; auto layout = LowLatencyTwoStageLayout(rdma_buffer_ptr, num_max_dispatch_tokens_per_rank, hidden, num_ranks, num_experts, num_topk); auto clean_meta_0 = layout.buffers[0].clean_meta(); auto clean_meta_1 = layout.buffers[1].clean_meta(); auto check_boundary = [=](void* ptr, size_t num_bytes) { auto offset = reinterpret_cast(ptr) - reinterpret_cast(rdma_buffer_ptr); EP_HOST_ASSERT(0 <= offset && offset + static_cast(num_bytes) <= num_rdma_bytes); }; check_boundary(clean_meta_0.first, clean_meta_0.second * sizeof(int)); check_boundary(clean_meta_1.first, clean_meta_1.second * sizeof(int)); internode_ll_two_stage::clean_low_latency_buffer_two_stage( buffer_ptrs_gpu, max_nvl_num_bytes, signal_bytes, nvl_rank, num_experts, clean_meta_0.first, clean_meta_0.second, clean_meta_1.first, clean_meta_1.second, calc_ctx->stream()); #else LOG(ERROR) << "NVSHMEM is not enabled. You can enable it by setting cmake " "option WITH_NVSHMEM=ON."; #endif } void Buffer::barrier_all() { #ifdef PADDLE_WITH_NVSHMEM internode_ll::barrier_all(calc_ctx->stream()); #else LOG(ERROR) << "NVSHMEM is not enabled. You can enable it by setting cmake " "option WITH_NVSHMEM=ON."; #endif } #ifdef PADDLE_WITH_NVSHMEM std::tuple, deep_ep::detail::Tensor, deep_ep::detail::Tensor, deep_ep::detail::Tensor, std::optional, std::optional>> Buffer::low_latency_dispatch( const deep_ep::detail::Tensor& x, const deep_ep::detail::Tensor& topk_idx, const std::optional& expertwise_scale, int num_max_dispatch_tokens_per_rank, int num_experts, bool use_fp8, bool async, bool return_recv_hook, int num_per_channel) { EP_HOST_ASSERT(low_latency_mode); // Tensor checks // By default using `ptp128c` FP8 cast EP_HOST_ASSERT(x.dim() == 2 && x.is_contiguous() && x.scalar_type() == deep_ep::detail::kBFloat16); EP_HOST_ASSERT(x.size(1) % sizeof(int4) == 0 && x.size(1) % 128 == 0); EP_HOST_ASSERT(topk_idx.dim() == 2 && topk_idx.is_contiguous()); EP_HOST_ASSERT(x.size(0) == topk_idx.size(0) && x.size(0) <= num_max_dispatch_tokens_per_rank); EP_HOST_ASSERT(topk_idx.scalar_type() == deep_ep::detail::kInt64); EP_HOST_ASSERT(num_experts % num_ranks == 0); auto num_tokens = static_cast(x.size(0)), hidden = static_cast(x.size(1)); auto num_scales = num_per_channel == -1 ? 1 : hidden / 128, num_topk = static_cast(topk_idx.size(1)); int num_local_experts = num_experts / num_ranks; // Buffer control LowLatencyLayout layout(rdma_buffer_ptr, num_max_dispatch_tokens_per_rank, hidden, num_ranks, num_experts); EP_HOST_ASSERT(static_cast(layout.total_bytes) <= num_rdma_bytes); auto buffer = layout.buffers[low_latency_buffer_idx]; auto next_buffer = layout.buffers[low_latency_buffer_idx ^= 1]; // Wait previous tasks to be finished // NOTES: the hook mode will always use the default stream auto compute_stream = calc_ctx->stream(); auto launch_stream = return_recv_hook ? compute_stream : comm_stream; EP_HOST_ASSERT(!(async && return_recv_hook)); if (!return_recv_hook) stream_wait(launch_stream, compute_stream); auto return_x_dtype = phi::DataType::BFLOAT16; if (use_fp8) { if (expertwise_scale.has_value()) { EP_HOST_ASSERT(expertwise_scale.value().size(0) == num_experts); } return_x_dtype = phi::DataType::FLOAT8_E4M3FN; } else if (expertwise_scale.has_value()) { EP_HOST_ASSERT(expertwise_scale.value().size(0) == num_experts); return_x_dtype = phi::DataType::INT8; } // Allocate packed tensors auto packed_recv_x = ConvertPaddleTensorToDetailTensor( paddle::experimental::empty({num_local_experts, num_ranks * num_max_dispatch_tokens_per_rank, hidden}, return_x_dtype, x.place())); auto packed_recv_src_info = ConvertPaddleTensorToDetailTensor(paddle::experimental::empty( {num_local_experts, num_ranks * num_max_dispatch_tokens_per_rank}, phi::DataType::INT32, phi::GPUPlace(device_id))); auto packed_recv_layout_range = ConvertPaddleTensorToDetailTensor( paddle::experimental::empty({num_local_experts, num_ranks}, phi::DataType::INT64, phi::GPUPlace(device_id))); auto packed_recv_count = ConvertPaddleTensorToDetailTensor(paddle::experimental::empty( {num_local_experts}, phi::DataType::INT32, phi::GPUPlace(device_id))); // Allocate column-majored scales auto packed_recv_x_scales = std::optional(); float* packed_recv_x_scales_ptr = nullptr; if (use_fp8 && !expertwise_scale.has_value()) { EP_HOST_ASSERT((num_ranks * num_max_dispatch_tokens_per_rank) % 4 == 0 && "TMA requires the number of tokens to be multiple of 4"); packed_recv_x_scales = ConvertPaddleTensorToDetailTensor(paddle::experimental::empty( {num_local_experts, num_scales, num_ranks * num_max_dispatch_tokens_per_rank}, phi::DataType::FLOAT32, phi::GPUPlace(device_id))); packed_recv_x_scales = ConvertPaddleTensorToDetailTensor(paddle::experimental::transpose( ConvertDetailTensorToPaddleTensor(packed_recv_x_scales.value()), std::vector{0, 2, 1})); packed_recv_x_scales_ptr = packed_recv_x_scales.value().data_ptr(); } float* expertwise_scale_ptr = nullptr; if (expertwise_scale.has_value()) { expertwise_scale_ptr = expertwise_scale.value().data_ptr(); } // Kernel launch auto next_clean_meta = next_buffer.clean_meta(); auto launcher = [=](int phases) { internode_ll::dispatch(packed_recv_x.data_ptr(), packed_recv_x_scales_ptr, packed_recv_src_info.data_ptr(), packed_recv_layout_range.data_ptr(), packed_recv_count.data_ptr(), buffer.dispatch_rdma_recv_data_buffer, buffer.dispatch_rdma_recv_count_buffer, buffer.dispatch_rdma_send_buffer, x.data_ptr(), topk_idx.data_ptr(), expertwise_scale_ptr, next_clean_meta.first, next_clean_meta.second, num_tokens, hidden, num_max_dispatch_tokens_per_rank, num_topk, num_experts, rank, num_ranks, use_fp8, workspace, launch_stream, phases, num_per_channel); }; launcher(return_recv_hook ? LOW_LATENCY_SEND_PHASE : (LOW_LATENCY_SEND_PHASE | LOW_LATENCY_RECV_PHASE)); // Wait streams std::optional event; if (async) { // NOTES: we must ensure the all tensors will not be deallocated before the // stream-wait happens, so in Python API, we must wrap all tensors into the // event handle. event = EventHandle(launch_stream); } else if (!return_recv_hook) { stream_wait(compute_stream, launch_stream); } // Receiver callback std::optional> recv_hook = std::nullopt; if (return_recv_hook) recv_hook = [=]() { launcher(LOW_LATENCY_RECV_PHASE); }; // Return values return {packed_recv_x, packed_recv_x_scales, packed_recv_count, packed_recv_src_info, packed_recv_layout_range, event, recv_hook}; } std::tuple, std::optional>> Buffer::low_latency_combine(const deep_ep::detail::Tensor& x, const deep_ep::detail::Tensor& topk_idx, const deep_ep::detail::Tensor& topk_weights, const deep_ep::detail::Tensor& src_info, const deep_ep::detail::Tensor& layout_range, int num_max_dispatch_tokens_per_rank, int num_experts, bool zero_copy, bool async, bool return_recv_hook, const std::optional& out) { EP_HOST_ASSERT(low_latency_mode); // Tensor checks EP_HOST_ASSERT(x.dim() == 3 && x.is_contiguous() && x.scalar_type() == deep_ep::detail::kBFloat16); EP_HOST_ASSERT(x.size(0) == num_experts / num_ranks); EP_HOST_ASSERT(x.size(1) == num_ranks * num_max_dispatch_tokens_per_rank); EP_HOST_ASSERT(x.size(2) % sizeof(int4) == 0 && x.size(2) % 128 == 0); EP_HOST_ASSERT(topk_idx.dim() == 2 && topk_idx.is_contiguous()); EP_HOST_ASSERT(topk_idx.size(0) == topk_weights.size(0) && topk_idx.size(1) == topk_weights.size(1)); EP_HOST_ASSERT(topk_idx.scalar_type() == deep_ep::detail::kInt64); EP_HOST_ASSERT(topk_weights.dim() == 2 && topk_weights.is_contiguous()); EP_HOST_ASSERT(topk_weights.size(0) <= num_max_dispatch_tokens_per_rank); EP_HOST_ASSERT(topk_weights.scalar_type() == deep_ep::detail::kFloat32); EP_HOST_ASSERT(src_info.dim() == 2 && src_info.is_contiguous()); EP_HOST_ASSERT(src_info.scalar_type() == deep_ep::detail::kInt32 && x.size(0) == src_info.size(0)); EP_HOST_ASSERT(layout_range.dim() == 2 && layout_range.is_contiguous()); EP_HOST_ASSERT(layout_range.scalar_type() == deep_ep::detail::kInt64); EP_HOST_ASSERT(layout_range.size(0) == num_experts / num_ranks && layout_range.size(1) == num_ranks); auto hidden = static_cast(x.size(2)); auto num_local_experts = num_experts / num_ranks, num_topk = static_cast(topk_weights.size(1)); (void)num_local_experts; auto num_combined_tokens = static_cast(topk_weights.size(0)); // Buffer control LowLatencyLayout layout(rdma_buffer_ptr, num_max_dispatch_tokens_per_rank, hidden, num_ranks, num_experts); EP_HOST_ASSERT(static_cast(layout.total_bytes) <= num_rdma_bytes); auto buffer = layout.buffers[low_latency_buffer_idx]; auto next_buffer = layout.buffers[low_latency_buffer_idx ^= 1]; // Wait previous tasks to be finished // NOTES: the hook mode will always use the default stream auto compute_stream = calc_ctx->stream(); auto launch_stream = return_recv_hook ? compute_stream : comm_stream; EP_HOST_ASSERT(!(async && return_recv_hook)); if (!return_recv_hook) stream_wait(launch_stream, compute_stream); // Allocate output tensor deep_ep::detail::Tensor combined_x; if (out.has_value()) { EP_HOST_ASSERT(out->dim() == 2 && out->is_contiguous()); EP_HOST_ASSERT(out->size(0) == num_combined_tokens && out->size(1) == hidden); EP_HOST_ASSERT(out->scalar_type() == x.scalar_type()); combined_x = out.value(); } else { combined_x = ConvertPaddleTensorToDetailTensor(paddle::experimental::empty( {num_combined_tokens, hidden}, x.dtype(), x.place())); } // Kernel launch auto next_clean_meta = next_buffer.clean_meta(); auto launcher = [=](int phases) { internode_ll::combine(combined_x.data_ptr(), buffer.combine_rdma_recv_data_buffer, buffer.combine_rdma_recv_flag_buffer, buffer.combine_rdma_send_buffer, x.data_ptr(), topk_idx.data_ptr(), topk_weights.data_ptr(), src_info.data_ptr(), layout_range.data_ptr(), next_clean_meta.first, next_clean_meta.second, num_combined_tokens, hidden, num_max_dispatch_tokens_per_rank, num_topk, num_experts, rank, num_ranks, workspace, launch_stream, phases, zero_copy); }; launcher(return_recv_hook ? LOW_LATENCY_SEND_PHASE : (LOW_LATENCY_SEND_PHASE | LOW_LATENCY_RECV_PHASE)); // Wait streams std::optional event; if (async) { // NOTES: we must ensure the all tensors will not be deallocated before the // stream-wait happens, so in Python API, we must wrap all tensors into the // event handle. event = EventHandle(launch_stream); } else if (!return_recv_hook) { stream_wait(compute_stream, launch_stream); } // Receiver callback std::optional> recv_hook = std::nullopt; if (return_recv_hook) recv_hook = [=]() { launcher(LOW_LATENCY_RECV_PHASE); }; // Return values return std::tuple, std::optional>>{ deep_ep::detail::Tensor{combined_x}, event, recv_hook}; } std::tuple, deep_ep::detail::Tensor, deep_ep::detail::Tensor, deep_ep::detail::Tensor, deep_ep::detail::Tensor, deep_ep::detail::Tensor, deep_ep::detail::Tensor, std::optional, std::optional>> Buffer::low_latency_dispatch_two_stage( const deep_ep::detail::Tensor& x, const deep_ep::detail::Tensor& topk_idx, const deep_ep::detail::Tensor& topk_weights, int num_max_dispatch_tokens_per_rank, int num_experts, bool use_fp8, bool async, bool return_recv_hook, int num_per_channel) { EP_HOST_ASSERT(low_latency_mode); // Tensor checks EP_HOST_ASSERT(x.dim() == 2 && x.is_contiguous() && x.scalar_type() == deep_ep::detail::kBFloat16); EP_HOST_ASSERT(x.size(1) % sizeof(int4) == 0 && x.size(1) % 128 == 0); EP_HOST_ASSERT(topk_idx.dim() == 2 && topk_idx.is_contiguous()); EP_HOST_ASSERT(x.size(0) == topk_idx.size(0) && x.size(0) <= num_max_dispatch_tokens_per_rank); EP_HOST_ASSERT(topk_idx.scalar_type() == deep_ep::detail::kInt64); EP_HOST_ASSERT(num_experts % num_ranks == 0); auto num_tokens = static_cast(x.size(0)), hidden = static_cast(x.size(1)); auto num_scales = num_per_channel == -1 ? 1 : hidden / 128, num_topk = static_cast(topk_idx.size(1)); int num_local_experts = num_experts / num_ranks; // Buffer control LowLatencyTwoStageLayout layout(rdma_buffer_ptr, num_max_dispatch_tokens_per_rank, hidden, num_ranks, num_experts, num_topk); EP_HOST_ASSERT(layout.total_bytes <= num_rdma_bytes); // fixed buffer, 0 for dispatch, 1 for combine auto buffer = layout.buffers[low_latency_buffer_idx]; auto next_buffer = layout.buffers[low_latency_buffer_idx ^= 1]; // Wait previous tasks to be finished auto compute_stream = calc_ctx->stream(); auto launch_stream = async ? comm_stream : compute_stream; EP_HOST_ASSERT(!(async && return_recv_hook)); auto return_x_dtype = phi::DataType::BFLOAT16; if (use_fp8) { return_x_dtype = phi::DataType::FLOAT8_E4M3FN; } // Allocate packed tensors auto packed_recv_x = ConvertPaddleTensorToDetailTensor( paddle::experimental::empty({num_local_experts, num_ranks * num_max_dispatch_tokens_per_rank, hidden}, return_x_dtype, x.place())); auto rdma_send_flags = ConvertPaddleTensorToDetailTensor( paddle::experimental::empty({num_tokens, num_ranks / NUM_MAX_NVL_PEERS}, phi::DataType::BOOL, phi::GPUPlace(device_id))); auto packed_recv_src_info = ConvertPaddleTensorToDetailTensor(paddle::experimental::empty( {num_local_experts, num_ranks * num_max_dispatch_tokens_per_rank}, phi::DataType::INT32, phi::GPUPlace(device_id))); auto packed_recv_layout_range = ConvertPaddleTensorToDetailTensor( paddle::experimental::empty({num_local_experts, num_ranks}, phi::DataType::INT64, phi::GPUPlace(device_id))); auto packed_recv_count = ConvertPaddleTensorToDetailTensor(paddle::experimental::empty( {num_local_experts}, phi::DataType::INT32, phi::GPUPlace(device_id))); auto packed_rdma_recv_count = ConvertPaddleTensorToDetailTensor( paddle::experimental::empty({num_ranks / NUM_MAX_NVL_PEERS}, phi::DataType::INT32, phi::GPUPlace(device_id))); const size_t num_bytes_per_msg = sizeof(int4) + (num_ranks / NUM_MAX_NVL_PEERS * (num_topk * 3 + 1) * sizeof(int) + sizeof(int4) - 1) / sizeof(int4) * sizeof(int4) + (use_fp8 ? (hidden + (num_scales + 3) / 4 * 4 * sizeof(float)) : (hidden * sizeof(nv_bfloat16))); auto packed_rdma_recv_x = ConvertPaddleTensorToDetailTensor( paddle::experimental::empty({num_ranks / NUM_MAX_NVL_PEERS, num_max_dispatch_tokens_per_rank, num_bytes_per_msg}, phi::DataType::UINT8, phi::GPUPlace(device_id))); // Allocate column-majored scales auto packed_recv_x_scales = std::optional(); float* packed_recv_x_scales_ptr = nullptr; if (use_fp8) { EP_HOST_ASSERT((num_ranks * num_max_dispatch_tokens_per_rank) % 4 == 0 && "TMA requires the number of tokens to be multiple of 4"); packed_recv_x_scales = ConvertPaddleTensorToDetailTensor(paddle::experimental::empty( {num_local_experts, num_scales, num_ranks * num_max_dispatch_tokens_per_rank}, phi::DataType::FLOAT32, phi::GPUPlace(device_id))); packed_recv_x_scales = ConvertPaddleTensorToDetailTensor(paddle::experimental::transpose( ConvertDetailTensorToPaddleTensor(packed_recv_x_scales.value()), std::vector{0, 2, 1})); packed_recv_x_scales_ptr = packed_recv_x_scales.value().data_ptr(); } // Kernel launch auto next_clean_meta = next_buffer.clean_meta(); auto launcher = [=](int phases) { internode_ll_two_stage::dispatch( packed_recv_x.data_ptr(), packed_recv_x_scales_ptr, packed_rdma_recv_x.data_ptr(), packed_recv_src_info.data_ptr(), packed_recv_layout_range.data_ptr(), packed_recv_count.data_ptr(), packed_rdma_recv_count.data_ptr(), rdma_send_flags.data_ptr(), buffer.dispatch_rdma_recv_data_buffer, buffer.dispatch_rdma_recv_count_buffer, buffer.dispatch_rdma_send_buffer, buffer_ptrs_gpu, x.data_ptr(), topk_idx.data_ptr(), topk_weights.data_ptr(), next_clean_meta.first, next_clean_meta.second, num_tokens, hidden, num_max_dispatch_tokens_per_rank, num_topk, num_experts, rank, num_ranks, use_fp8, workspace, launch_stream, phases, low_latency_buffer_idx, num_per_channel); }; launcher(return_recv_hook ? LOW_LATENCY_SEND_PHASE : (LOW_LATENCY_SEND_PHASE | LOW_LATENCY_RECV_PHASE)); // Async event std::optional event; if (async) { event = EventHandle(launch_stream); } std::optional> recv_hook = std::nullopt; if (return_recv_hook) recv_hook = [=]() { launcher(LOW_LATENCY_RECV_PHASE); }; return {packed_recv_x, packed_recv_x_scales, packed_rdma_recv_x, packed_recv_count, packed_rdma_recv_count, packed_recv_src_info, packed_recv_layout_range, rdma_send_flags, event, recv_hook}; } std::tuple, std::optional>> Buffer::low_latency_combine_two_stage( const deep_ep::detail::Tensor& x, const deep_ep::detail::Tensor& rdma_recv_x, const deep_ep::detail::Tensor& topk_idx, const deep_ep::detail::Tensor& topk_weights, const deep_ep::detail::Tensor& src_info, const deep_ep::detail::Tensor& layout_range, const deep_ep::detail::Tensor& rdma_send_flags, const deep_ep::detail::Tensor& dispatch_rdma_recv_count, int num_max_dispatch_tokens_per_rank, int num_experts, bool dispatch_use_fp8, bool async, bool return_recv_hook, int num_per_channel, const std::optional& out) { EP_HOST_ASSERT(low_latency_mode); // Tensor checks EP_HOST_ASSERT(x.dim() == 3 && x.is_contiguous() && x.scalar_type() == deep_ep::detail::kBFloat16); EP_HOST_ASSERT(x.size(0) == num_experts / num_ranks); EP_HOST_ASSERT(x.size(1) == num_ranks * num_max_dispatch_tokens_per_rank); EP_HOST_ASSERT(x.size(2) % sizeof(int4) == 0 && x.size(2) % 128 == 0); EP_HOST_ASSERT(topk_idx.dim() == 2 && topk_idx.is_contiguous()); EP_HOST_ASSERT(topk_idx.size(0) == topk_weights.size(0) && topk_idx.size(1) == topk_weights.size(1)); EP_HOST_ASSERT(topk_idx.scalar_type() == deep_ep::detail::kInt64); EP_HOST_ASSERT(topk_weights.dim() == 2 && topk_weights.is_contiguous()); EP_HOST_ASSERT(topk_weights.size(0) <= num_max_dispatch_tokens_per_rank); EP_HOST_ASSERT(topk_weights.scalar_type() == deep_ep::detail::kFloat32); EP_HOST_ASSERT(src_info.dim() == 2 && src_info.is_contiguous()); EP_HOST_ASSERT(src_info.scalar_type() == deep_ep::detail::kInt32 && x.size(0) == src_info.size(0)); EP_HOST_ASSERT(layout_range.dim() == 2 && layout_range.is_contiguous()); EP_HOST_ASSERT(layout_range.scalar_type() == deep_ep::detail::kInt64); EP_HOST_ASSERT(layout_range.size(0) == num_experts / num_ranks && layout_range.size(1) == num_ranks); auto hidden = static_cast(x.size(2)); auto num_local_experts = num_experts / num_ranks, num_topk = static_cast(topk_weights.size(1)); auto num_combined_tokens = static_cast(topk_weights.size(0)); // Buffer control LowLatencyTwoStageLayout layout(rdma_buffer_ptr, num_max_dispatch_tokens_per_rank, hidden, num_ranks, num_experts, num_topk); EP_HOST_ASSERT(layout.total_bytes <= num_rdma_bytes); auto buffer = layout.buffers[low_latency_buffer_idx]; auto next_buffer = layout.buffers[low_latency_buffer_idx ^= 1]; auto compute_stream = calc_ctx->stream(); auto launch_stream = async ? comm_stream : compute_stream; EP_HOST_ASSERT(!(async && return_recv_hook)); // Allocate output tensor deep_ep::detail::Tensor combined_x; if (out.has_value()) { EP_HOST_ASSERT(out->dim() == 2 && out->is_contiguous()); EP_HOST_ASSERT(out->size(0) == num_combined_tokens && out->size(1) == hidden); EP_HOST_ASSERT(out->scalar_type() == x.scalar_type()); combined_x = out.value(); } else { combined_x = ConvertPaddleTensorToDetailTensor(paddle::experimental::empty( {num_combined_tokens, hidden}, x.dtype(), x.place())); } // Kernel launch auto next_clean_meta = next_buffer.clean_meta(); auto launcher = [=](int phases) { internode_ll_two_stage::combine(combined_x.data_ptr(), buffer.combine_rdma_recv_data_buffer, buffer.combine_rdma_recv_flag_buffer, buffer.combine_rdma_send_buffer, rdma_recv_x.data_ptr(), dispatch_rdma_recv_count.data_ptr(), buffer_ptrs_gpu, x.data_ptr(), topk_idx.data_ptr(), topk_weights.data_ptr(), src_info.data_ptr(), layout_range.data_ptr(), rdma_send_flags.data_ptr(), next_clean_meta.first, next_clean_meta.second, num_combined_tokens, hidden, num_max_dispatch_tokens_per_rank, num_topk, num_experts, rank, num_ranks, workspace, launch_stream, phases, dispatch_use_fp8, low_latency_buffer_idx, num_per_channel); }; launcher(return_recv_hook ? LOW_LATENCY_SEND_PHASE : (LOW_LATENCY_SEND_PHASE | LOW_LATENCY_RECV_PHASE)); // Async event std::optional event; if (async) { event = EventHandle(launch_stream); } // Receiver callback std::optional> recv_hook = std::nullopt; if (return_recv_hook) recv_hook = [=]() { launcher(LOW_LATENCY_RECV_PHASE); }; // Return values return {combined_x, event, recv_hook}; } std::tuple, deep_ep::detail::Tensor, deep_ep::detail::Tensor, deep_ep::detail::Tensor, deep_ep::detail::Tensor, deep_ep::detail::Tensor, deep_ep::detail::Tensor, std::optional, std::optional>> Buffer::m2n_low_latency_dispatch_two_stage( const deep_ep::detail::Tensor& x, const deep_ep::detail::Tensor& topk_idx, const deep_ep::detail::Tensor& topk_weights, int num_max_dispatch_tokens_per_rank, int num_experts, int a_start_rank, int a_num_ranks, int e_start_rank, int e_num_ranks, bool use_fp8, bool async, bool return_recv_hook) { EP_HOST_ASSERT(low_latency_mode); // Tensor checks EP_HOST_ASSERT(x.dim() == 2 && x.is_contiguous() && x.scalar_type() == deep_ep::detail::kBFloat16); EP_HOST_ASSERT(x.size(1) % sizeof(int4) == 0 && x.size(1) % 128 == 0); EP_HOST_ASSERT(topk_idx.dim() == 2 && topk_idx.is_contiguous()); EP_HOST_ASSERT(x.size(0) == topk_idx.size(0) && x.size(0) <= num_max_dispatch_tokens_per_rank); EP_HOST_ASSERT(topk_idx.scalar_type() == deep_ep::detail::kInt64); EP_HOST_ASSERT(num_experts % num_ranks == 0); auto num_tokens = static_cast(x.size(0)), hidden = static_cast(x.size(1)); auto num_scales = hidden / 128, num_topk = static_cast(topk_idx.size(1)); int num_local_experts = num_experts / num_ranks; // Buffer control LowLatencyTwoStageLayout layout(rdma_buffer_ptr, num_max_dispatch_tokens_per_rank, hidden, num_ranks, num_experts, num_topk); EP_HOST_ASSERT(layout.total_bytes <= num_rdma_bytes); // fixed buffer, 0 for dispatch, 1 for combine auto buffer = layout.buffers[0]; auto next_buffer = layout.buffers[1]; auto dispatch_workspace = reinterpret_cast( reinterpret_cast(workspace) + m2n_ll_dispatch_workspace_idx * NUM_WORKSPACE_BYTES); m2n_ll_dispatch_workspace_idx = (m2n_ll_dispatch_workspace_idx + 1) % M2N_NUM_WORKSPACE; auto dispatch_rdma_recv_complete = buffer.dispatch_rdma_recv_complete_buffer + m2n_ll_dispatch_recv_complete_idx * num_ranks; m2n_ll_dispatch_recv_complete_idx = (m2n_ll_dispatch_recv_complete_idx + 1) % M2N_NUM_MAX_MICRO_BATCHES; // Wait previous tasks to be finished // NOTES: the hook mode will always use the default stream // auto compute_stream = calc_ctx->stream(); // auto launch_stream = return_recv_hook ? compute_stream : comm_stream; // EP_HOST_ASSERT(!(async && return_recv_hook)); // if (!return_recv_hook) stream_wait(launch_stream, compute_stream); auto compute_stream = calc_ctx->stream(); auto launch_stream = comm_stream; if (rank >= a_start_rank && rank < a_start_rank + a_num_ranks) { stream_wait(launch_stream, compute_stream); } if (rank >= a_start_rank && rank < a_start_rank + a_num_ranks) { stream_wait(compute_stream, launch_stream); } auto return_x_dtype = phi::DataType::BFLOAT16; if (use_fp8) { return_x_dtype = phi::DataType::FLOAT8_E4M3FN; } // Allocate packed tensors auto packed_recv_x = ConvertPaddleTensorToDetailTensor( paddle::experimental::empty({num_local_experts, num_ranks * num_max_dispatch_tokens_per_rank, hidden}, return_x_dtype, x.place())); auto rdma_send_flags = ConvertPaddleTensorToDetailTensor( paddle::experimental::empty({num_tokens, num_ranks / NUM_MAX_NVL_PEERS}, phi::DataType::BOOL, phi::GPUPlace(device_id))); auto packed_recv_src_info = ConvertPaddleTensorToDetailTensor(paddle::experimental::empty( {num_local_experts, num_ranks * num_max_dispatch_tokens_per_rank}, phi::DataType::INT32, phi::GPUPlace(device_id))); auto packed_recv_layout_range = ConvertPaddleTensorToDetailTensor( paddle::experimental::empty({num_local_experts, num_ranks}, phi::DataType::INT64, phi::GPUPlace(device_id))); auto packed_recv_count = ConvertPaddleTensorToDetailTensor(paddle::experimental::empty( {num_local_experts}, phi::DataType::INT32, phi::GPUPlace(device_id))); auto packed_rdma_recv_count = ConvertPaddleTensorToDetailTensor( paddle::experimental::empty({num_ranks / NUM_MAX_NVL_PEERS}, phi::DataType::INT32, phi::GPUPlace(device_id))); const size_t num_bytes_per_msg = sizeof(int4) + (num_ranks / NUM_MAX_NVL_PEERS * (num_topk * 3 + 1) * sizeof(int) + sizeof(int4) - 1) / sizeof(int4) * sizeof(int4) + (use_fp8 ? (hidden + num_scales * sizeof(float)) : (hidden * sizeof(nv_bfloat16))); auto packed_rdma_recv_x = ConvertPaddleTensorToDetailTensor( paddle::experimental::empty({num_ranks / NUM_MAX_NVL_PEERS, num_max_dispatch_tokens_per_rank, num_bytes_per_msg}, phi::DataType::UINT8, phi::GPUPlace(device_id))); // Allocate column-majored scales auto packed_recv_x_scales = std::optional(); float* packed_recv_x_scales_ptr = nullptr; if (use_fp8) { EP_HOST_ASSERT((num_ranks * num_max_dispatch_tokens_per_rank) % 4 == 0 && "TMA requires the number of tokens to be multiple of 4"); packed_recv_x_scales = ConvertPaddleTensorToDetailTensor(paddle::experimental::empty( {num_local_experts, num_scales, num_ranks * num_max_dispatch_tokens_per_rank}, phi::DataType::FLOAT32, phi::GPUPlace(device_id))); packed_recv_x_scales = ConvertPaddleTensorToDetailTensor(paddle::experimental::transpose( ConvertDetailTensorToPaddleTensor(packed_recv_x_scales.value()), std::vector{0, 2, 1})); packed_recv_x_scales_ptr = packed_recv_x_scales.value().data_ptr(); } // Kernel launch auto next_clean_meta = next_buffer.clean_meta(); auto launcher = [=](int phases) { m2n_ll_two_stage::dispatch(packed_recv_x.data_ptr(), packed_recv_x_scales_ptr, packed_rdma_recv_x.data_ptr(), packed_recv_src_info.data_ptr(), packed_recv_layout_range.data_ptr(), packed_recv_count.data_ptr(), packed_rdma_recv_count.data_ptr(), rdma_send_flags.data_ptr(), buffer.dispatch_rdma_recv_data_buffer, buffer.dispatch_rdma_recv_count_buffer, dispatch_rdma_recv_complete, buffer.dispatch_rdma_send_buffer, buffer_ptrs_gpu, x.data_ptr(), topk_idx.data_ptr(), topk_weights.data_ptr(), next_clean_meta.first, next_clean_meta.second, num_tokens, hidden, num_max_dispatch_tokens_per_rank, num_topk, num_experts, rank, num_ranks, a_start_rank, a_num_ranks, e_start_rank, e_num_ranks, use_fp8, dispatch_workspace, launch_stream, phases); }; // TODO(Zhenyu Li): supports async/return_recv_hook launcher(return_recv_hook ? LOW_LATENCY_SEND_PHASE : (LOW_LATENCY_SEND_PHASE | LOW_LATENCY_RECV_PHASE)); // Wait streams // std::optional event; // if (async) { // // NOTES: we must ensure the all tensors will not be deallocated before // the // // stream-wait happens, so in Python API, we must wrap all tensors into // the // // event handle. // event = EventHandle(launch_stream); // } else if (!return_recv_hook) { // stream_wait(compute_stream, launch_stream); // } std::optional event; if (async) { // NOTES: we must ensure the all tensors will not be deallocated before the // stream-wait happens, so in Python API, we must wrap all tensors into the // event handle. event = EventHandle(launch_stream); } // // stream_wait(launch_stream, compute_stream); // if (rank >= a_start_rank && rank < a_start_rank + a_num_ranks) { // stream_wait(compute_stream, launch_stream); // } // Receiver callback std::optional> recv_hook = std::nullopt; if (return_recv_hook) recv_hook = [=]() { // stream_wait(launch_stream, compute_stream); launcher(LOW_LATENCY_RECV_PHASE); // stream_wait(compute_stream, launch_stream); // if (rank >= e_start_rank && rank < e_start_rank + e_num_ranks) { // stream_wait(compute_stream, launch_stream); // } return EventHandle(launch_stream); }; return {packed_recv_x, packed_recv_x_scales, packed_rdma_recv_x, packed_recv_count, packed_rdma_recv_count, packed_recv_src_info, packed_recv_layout_range, rdma_send_flags, event, recv_hook}; } std::tuple, std::optional>> Buffer::m2n_low_latency_combine_two_stage( const deep_ep::detail::Tensor& x, const deep_ep::detail::Tensor& rdma_recv_x, const deep_ep::detail::Tensor& topk_idx, const deep_ep::detail::Tensor& topk_weights, const deep_ep::detail::Tensor& src_info, const deep_ep::detail::Tensor& layout_range, const deep_ep::detail::Tensor& rdma_send_flags, const deep_ep::detail::Tensor& dispatch_rdma_recv_count, int num_max_dispatch_tokens_per_rank, int num_experts, int a_start_rank, int a_num_ranks, int e_start_rank, int e_num_ranks, bool dispatch_use_fp8, bool async, bool return_recv_hook, const std::optional& out) { EP_HOST_ASSERT(low_latency_mode); // Tensor checks EP_HOST_ASSERT(x.dim() == 3 && x.is_contiguous() && x.scalar_type() == deep_ep::detail::kBFloat16); EP_HOST_ASSERT(x.size(0) == num_experts / num_ranks); EP_HOST_ASSERT(x.size(1) == num_ranks * num_max_dispatch_tokens_per_rank); EP_HOST_ASSERT(x.size(2) % sizeof(int4) == 0 && x.size(2) % 128 == 0); EP_HOST_ASSERT(topk_idx.dim() == 2 && topk_idx.is_contiguous()); EP_HOST_ASSERT(topk_idx.size(0) == topk_weights.size(0) && topk_idx.size(1) == topk_weights.size(1)); EP_HOST_ASSERT(topk_idx.scalar_type() == deep_ep::detail::kInt64); EP_HOST_ASSERT(topk_weights.dim() == 2 && topk_weights.is_contiguous()); EP_HOST_ASSERT(topk_weights.size(0) <= num_max_dispatch_tokens_per_rank); EP_HOST_ASSERT(topk_weights.scalar_type() == deep_ep::detail::kFloat32); EP_HOST_ASSERT(src_info.dim() == 2 && src_info.is_contiguous()); EP_HOST_ASSERT(src_info.scalar_type() == deep_ep::detail::kInt32 && x.size(0) == src_info.size(0)); EP_HOST_ASSERT(layout_range.dim() == 2 && layout_range.is_contiguous()); EP_HOST_ASSERT(layout_range.scalar_type() == deep_ep::detail::kInt64); EP_HOST_ASSERT(layout_range.size(0) == num_experts / num_ranks && layout_range.size(1) == num_ranks); auto hidden = static_cast(x.size(2)); auto num_local_experts = num_experts / num_ranks, num_topk = static_cast(topk_weights.size(1)); auto num_combined_tokens = static_cast(topk_weights.size(0)); // Buffer control LowLatencyTwoStageLayout layout(rdma_buffer_ptr, num_max_dispatch_tokens_per_rank, hidden, num_ranks, num_experts, num_topk); EP_HOST_ASSERT(layout.total_bytes <= num_rdma_bytes); // fixed buffer, 0 for dispatch, 1 for combine auto dispatch_buffer = layout.buffers[0]; auto buffer = layout.buffers[1]; auto next_buffer = layout.buffers[0]; auto combine_workspace = reinterpret_cast( reinterpret_cast(workspace) + (M2N_NUM_WORKSPACE + m2n_ll_combine_workspace_idx) * NUM_WORKSPACE_BYTES); m2n_ll_combine_workspace_idx = (m2n_ll_combine_workspace_idx + 1) % M2N_NUM_WORKSPACE; auto combine_rdma_recv_complete = buffer.combine_rdma_recv_complete_buffer + m2n_ll_combine_recv_complete_idx * num_ranks; m2n_ll_combine_recv_complete_idx = (m2n_ll_combine_recv_complete_idx + 1) % M2N_NUM_MAX_MICRO_BATCHES; // Wait previous tasks to be finished // NOTES: the hook mode will always use the default stream // auto compute_stream = calc_ctx->stream(); // auto launch_stream = return_recv_hook ? compute_stream : comm_stream; // EP_HOST_ASSERT(!(async && return_recv_hook)); // if (!return_recv_hook) stream_wait(launch_stream, compute_stream); auto compute_stream = calc_ctx->stream(); auto launch_stream = comm_stream; if (rank >= e_start_rank && rank < e_start_rank + e_num_ranks) { stream_wait(launch_stream, compute_stream); } if (rank >= e_start_rank && rank < e_start_rank + e_num_ranks) { stream_wait(compute_stream, launch_stream); } // Allocate output tensor deep_ep::detail::Tensor combined_x; if (out.has_value()) { EP_HOST_ASSERT(out->dim() == 2 && out->is_contiguous()); EP_HOST_ASSERT(out->size(0) == num_combined_tokens && out->size(1) == hidden); EP_HOST_ASSERT(out->scalar_type() == x.scalar_type()); combined_x = out.value(); } else { combined_x = ConvertPaddleTensorToDetailTensor(paddle::experimental::empty( {num_combined_tokens, hidden}, x.dtype(), x.place())); } // Kernel launch auto next_clean_meta = next_buffer.clean_meta(); auto launcher = [=](int phases) { m2n_ll_two_stage::combine(combined_x.data_ptr(), buffer.combine_rdma_recv_data_buffer, buffer.combine_rdma_recv_flag_buffer, buffer.combine_rdma_send_buffer, combine_rdma_recv_complete, rdma_recv_x.data_ptr(), dispatch_rdma_recv_count.data_ptr(), buffer_ptrs_gpu, x.data_ptr(), topk_idx.data_ptr(), topk_weights.data_ptr(), src_info.data_ptr(), layout_range.data_ptr(), rdma_send_flags.data_ptr(), next_clean_meta.first, next_clean_meta.second, num_combined_tokens, hidden, num_max_dispatch_tokens_per_rank, num_topk, num_experts, rank, num_ranks, a_start_rank, a_num_ranks, e_start_rank, e_num_ranks, combine_workspace, launch_stream, phases, dispatch_use_fp8); }; // TODO(Zhenyu Li): supports async/return_recv_hook launcher(return_recv_hook ? LOW_LATENCY_SEND_PHASE : (LOW_LATENCY_SEND_PHASE | LOW_LATENCY_RECV_PHASE)); // Wait streams // std::optional event; // if (async) { // // NOTES: we must ensure the all tensors will not be deallocated before // the // // stream-wait happens, so in Python API, we must wrap all tensors into // the // // event handle. // event = EventHandle(launch_stream); // } else if (!return_recv_hook) { // stream_wait(compute_stream, launch_stream); // } std::optional event; if (async) { // NOTES: we must ensure the all tensors will not be deallocated before the // stream-wait happens, so in Python API, we must wrap all tensors into the // event handle. event = EventHandle(launch_stream); } // // stream_wait(launch_stream, compute_stream); // if (rank >= e_start_rank && rank < e_start_rank + e_num_ranks) { // stream_wait(compute_stream, launch_stream); // } // Receiver callback std::optional> recv_hook = std::nullopt; if (return_recv_hook) recv_hook = [=]() { // stream_wait(launch_stream, compute_stream); launcher(LOW_LATENCY_RECV_PHASE); // stream_wait(compute_stream, launch_stream); // stream_wait(launch_stream, compute_stream); // if (rank >= a_start_rank && rank < a_start_rank + a_num_ranks) { // stream_wait(compute_stream, launch_stream); // } return EventHandle(launch_stream); }; // Return values return {combined_x, event, recv_hook}; } #endif // PADDLE_WITH_NVSHMEM std::tuple, std::optional, std::optional, std::vector, paddle::Tensor, paddle::Tensor, std::optional, paddle::Tensor, std::optional, paddle::Tensor, std::optional, std::optional, std::optional, std::optional> Buffer::internode_dispatch_api( const paddle::Tensor& x, const std::optional& x_scales, const std::optional& topk_idx, const std::optional& topk_weights, const std::optional& num_tokens_per_rank, const std::optional& num_tokens_per_rdma_rank, const paddle::Tensor& is_token_in_rank, const std::optional& num_tokens_per_expert, int cached_num_recv_tokens, int cached_num_rdma_recv_tokens, const std::optional& cached_rdma_channel_prefix_matrix, const std::optional& cached_recv_rdma_rank_prefix_sum, const std::optional& cached_gbl_channel_prefix_matrix, const std::optional& cached_recv_gbl_rank_prefix_sum, int expert_alignment, const Config& config, std::optional& previous_event, // NOLINT bool async, bool allocate_on_comm_stream) { #ifdef PADDLE_WITH_NVSHMEM const auto& x_ = ConvertPaddleTensorToDetailTensor(x); std::optional x_scales_ = ConvertOptionalPaddleTensorToDetailTensor(x_scales); std::optional topk_idx_ = ConvertOptionalPaddleTensorToDetailTensor(topk_idx); std::optional topk_weights_ = ConvertOptionalPaddleTensorToDetailTensor(topk_weights); std::optional num_tokens_per_rank_ = ConvertOptionalPaddleTensorToDetailTensor(num_tokens_per_rank); std::optional num_tokens_per_rdma_rank_ = ConvertOptionalPaddleTensorToDetailTensor(num_tokens_per_rdma_rank); const auto& is_token_in_rank_ = ConvertPaddleTensorToDetailTensor(is_token_in_rank); std::optional num_tokens_per_expert_ = ConvertOptionalPaddleTensorToDetailTensor(num_tokens_per_expert); std::optional cached_rdma_channel_prefix_matrix_ = ConvertOptionalPaddleTensorToDetailTensor( cached_rdma_channel_prefix_matrix); std::optional cached_recv_rdma_rank_prefix_sum_ = ConvertOptionalPaddleTensorToDetailTensor( cached_recv_rdma_rank_prefix_sum); std::optional cached_gbl_channel_prefix_matrix_ = ConvertOptionalPaddleTensorToDetailTensor( cached_gbl_channel_prefix_matrix); std::optional cached_recv_gbl_rank_prefix_sum_ = ConvertOptionalPaddleTensorToDetailTensor( cached_recv_gbl_rank_prefix_sum); auto res = internode_dispatch(x_, x_scales_, topk_idx_, topk_weights_, num_tokens_per_rank_, num_tokens_per_rdma_rank_, is_token_in_rank_, num_tokens_per_expert_, cached_num_recv_tokens, cached_num_rdma_recv_tokens, cached_rdma_channel_prefix_matrix_, cached_recv_rdma_rank_prefix_sum_, cached_gbl_channel_prefix_matrix_, cached_recv_gbl_rank_prefix_sum_, expert_alignment, config, previous_event, async, allocate_on_comm_stream); auto recv_x_ = ConvertDetailTensorToPaddleTensor(std::get<0>(res)); std::optional recv_x_scales_ = ConvertOptionalDetailTensorToPaddleTensor(std::get<1>(res)); std::optional recv_topk_idx_ = ConvertOptionalDetailTensorToPaddleTensor(std::get<2>(res)); std::optional recv_topk_weights_ = ConvertOptionalDetailTensorToPaddleTensor(std::get<3>(res)); const auto& num_recv_tokens_per_expert_list = std::get<4>(res); auto rdma_channel_prefix_matrix_ = ConvertDetailTensorToPaddleTensor(std::get<5>(res)); auto gbl_channel_prefix_matrix_ = ConvertDetailTensorToPaddleTensor(std::get<6>(res)); std::optional recv_rdma_channel_prefix_matrix_ = ConvertOptionalDetailTensorToPaddleTensor(std::get<7>(res)); auto recv_rdma_rank_prefix_sum_ = ConvertDetailTensorToPaddleTensor(std::get<8>(res)); std::optional recv_gbl_channel_prefix_matrix_ = ConvertOptionalDetailTensorToPaddleTensor(std::get<9>(res)); auto recv_gbl_rank_prefix_sum_ = ConvertDetailTensorToPaddleTensor(std::get<10>(res)); std::optional recv_src_meta_ = ConvertOptionalDetailTensorToPaddleTensor(std::get<11>(res)); std::optional send_rdma_head_ = ConvertOptionalDetailTensorToPaddleTensor(std::get<12>(res)); std::optional send_nvl_head_ = ConvertOptionalDetailTensorToPaddleTensor(std::get<13>(res)); const auto& event = std::get<14>(res); return {recv_x_, recv_x_scales_, recv_topk_idx_, recv_topk_weights_, num_recv_tokens_per_expert_list, rdma_channel_prefix_matrix_, gbl_channel_prefix_matrix_, recv_rdma_channel_prefix_matrix_, recv_rdma_rank_prefix_sum_, recv_gbl_channel_prefix_matrix_, recv_gbl_rank_prefix_sum_, recv_src_meta_, send_rdma_head_, send_nvl_head_, event}; #else LOG(ERROR) << "NVSHMEM is not enabled. You can enable it by setting cmake " "option WITH_NVSHMEM=ON."; return {}; #endif } std::tuple, std::optional> Buffer::internode_combine_api( const paddle::Tensor& x, const std::optional& topk_weights, const paddle::Tensor& src_meta, const paddle::Tensor& is_combined_token_in_rank, const paddle::Tensor& rdma_channel_prefix_matrix, const paddle::Tensor& rdma_rank_prefix_sum, const paddle::Tensor& gbl_channel_prefix_matrix, const paddle::Tensor& combined_rdma_head, const paddle::Tensor& combined_nvl_head, const Config& config, std::optional& previous_event, // NOLINT bool async, bool allocate_on_comm_stream) { #ifdef PADDLE_WITH_NVSHMEM const auto& x_ = ConvertPaddleTensorToDetailTensor(x); std::optional topk_weights_ = ConvertOptionalPaddleTensorToDetailTensor(topk_weights); const auto& src_meta_ = ConvertPaddleTensorToDetailTensor(src_meta); const auto& is_combined_token_in_rank_ = ConvertPaddleTensorToDetailTensor(is_combined_token_in_rank); const auto& rdma_channel_prefix_matrix_ = ConvertPaddleTensorToDetailTensor(rdma_channel_prefix_matrix); const auto& rdma_rank_prefix_sum_ = ConvertPaddleTensorToDetailTensor(rdma_rank_prefix_sum); const auto& gbl_channel_prefix_matrix_ = ConvertPaddleTensorToDetailTensor(gbl_channel_prefix_matrix); const auto& combined_rdma_head_ = ConvertPaddleTensorToDetailTensor(combined_rdma_head); const auto& combined_nvl_head_ = ConvertPaddleTensorToDetailTensor(combined_nvl_head); auto res = internode_combine(x_, topk_weights_, src_meta_, is_combined_token_in_rank_, rdma_channel_prefix_matrix_, rdma_rank_prefix_sum_, gbl_channel_prefix_matrix_, combined_rdma_head_, combined_nvl_head_, config, previous_event, async, allocate_on_comm_stream); auto combined_x_ = ConvertDetailTensorToPaddleTensor(std::get<0>(res)); std::optional combined_topk_weights_ = ConvertOptionalDetailTensorToPaddleTensor(std::get<1>(res)); const auto& event = std::get<2>(res); return {combined_x_, combined_topk_weights_, event}; #else LOG(ERROR) << "NVSHMEM is not enabled. You can enable it by setting cmake " "option WITH_NVSHMEM=ON."; return {}; #endif } std::tuple, paddle::Tensor, paddle::Tensor, paddle::Tensor, std::optional, std::optional>> Buffer::low_latency_dispatch_api( const paddle::Tensor& x, const paddle::Tensor& topk_idx, const std::optional& expertwise_scale, int num_max_dispatch_tokens_per_rank, int num_experts, bool use_fp8, bool async, bool return_recv_hook, int num_per_channel) { #ifdef PADDLE_WITH_NVSHMEM const auto& x_ = ConvertPaddleTensorToDetailTensor(x); const auto& topk_idx_ = ConvertPaddleTensorToDetailTensor(topk_idx); std::optional expertwise_scale_; if (expertwise_scale.has_value()) { expertwise_scale_ = ConvertPaddleTensorToDetailTensor(expertwise_scale.value()); } auto res = low_latency_dispatch(x_, topk_idx_, expertwise_scale_, num_max_dispatch_tokens_per_rank, num_experts, use_fp8, async, return_recv_hook, num_per_channel); auto packed_recv_x_ = ConvertDetailTensorToPaddleTensor(std::get<0>(res)); std::optional packed_recv_x_scales_; if (std::get<1>(res).has_value()) { packed_recv_x_scales_ = ConvertDetailTensorToPaddleTensor(std::get<1>(res).value()); } auto packed_recv_count_ = ConvertDetailTensorToPaddleTensor(std::get<2>(res)); auto packed_recv_src_info_ = ConvertDetailTensorToPaddleTensor(std::get<3>(res)); auto packed_recv_layout_range_ = ConvertDetailTensorToPaddleTensor(std::get<4>(res)); const auto& event = std::get<5>(res); auto recv_hook = std::get<6>(res); return {packed_recv_x_, packed_recv_x_scales_, packed_recv_count_, packed_recv_src_info_, packed_recv_layout_range_, event, recv_hook}; #else LOG(ERROR) << "NVSHMEM is not enabled. You can enable it by setting cmake " "option WITH_NVSHMEM=ON."; return {}; #endif } std::tuple, std::optional>> Buffer::low_latency_combine_api(const paddle::Tensor& x, const paddle::Tensor& topk_idx, const paddle::Tensor& topk_weights, const paddle::Tensor& src_info, const paddle::Tensor& layout_range, int num_max_dispatch_tokens_per_rank, int num_experts, bool zero_copy, bool async, bool return_recv_hook, const std::optional& out) { #ifdef PADDLE_WITH_NVSHMEM const auto& x_ = ConvertPaddleTensorToDetailTensor(x); const auto& topk_idx_ = ConvertPaddleTensorToDetailTensor(topk_idx); const auto& topk_weights_ = ConvertPaddleTensorToDetailTensor(topk_weights); const auto& src_info_ = ConvertPaddleTensorToDetailTensor(src_info); const auto& layout_range_ = ConvertPaddleTensorToDetailTensor(layout_range); std::optional out_ = std::nullopt; if (out.has_value()) { out_ = ConvertOptionalPaddleTensorToDetailTensor(out.value()); } auto res = low_latency_combine(x_, topk_idx_, topk_weights_, src_info_, layout_range_, num_max_dispatch_tokens_per_rank, num_experts, zero_copy, async, return_recv_hook, out_); auto combined_x_ = ConvertDetailTensorToPaddleTensor(std::get<0>(res)); const auto& event = std::get<1>(res); auto recv_hook = std::get<2>(res); return {combined_x_, event, recv_hook}; #else LOG(ERROR) << "NVSHMEM is not enabled. You can enable it by setting cmake " "option WITH_NVSHMEM=ON."; return {}; #endif } std::tuple, paddle::Tensor, paddle::Tensor, paddle::Tensor, paddle::Tensor, paddle::Tensor, paddle::Tensor, std::optional, std::optional>> Buffer::low_latency_dispatch_two_stage_api(const paddle::Tensor& x, const paddle::Tensor& topk_idx, const paddle::Tensor& topk_weights, int num_max_dispatch_tokens_per_rank, int num_experts, bool use_fp8, bool async, bool return_recv_hook, int num_per_channel) { #ifdef PADDLE_WITH_NVSHMEM const auto& x_ = ConvertPaddleTensorToDetailTensor(x); const auto& topk_idx_ = ConvertPaddleTensorToDetailTensor(topk_idx); const auto& topk_weights_ = ConvertPaddleTensorToDetailTensor(topk_weights); auto res = low_latency_dispatch_two_stage(x_, topk_idx_, topk_weights_, num_max_dispatch_tokens_per_rank, num_experts, use_fp8, async, return_recv_hook, num_per_channel); auto packed_recv_x_ = ConvertDetailTensorToPaddleTensor(std::get<0>(res)); std::optional packed_recv_x_scales_; if (std::get<1>(res).has_value()) { packed_recv_x_scales_ = ConvertDetailTensorToPaddleTensor(std::get<1>(res).value()); } auto packed_recv_rdma_x_ = ConvertDetailTensorToPaddleTensor(std::get<2>(res)); auto packed_recv_count_ = ConvertDetailTensorToPaddleTensor(std::get<3>(res)); auto packed_rdma_recv_count_ = ConvertDetailTensorToPaddleTensor(std::get<4>(res)); auto packed_recv_src_info_ = ConvertDetailTensorToPaddleTensor(std::get<5>(res)); auto packed_recv_layout_range_ = ConvertDetailTensorToPaddleTensor(std::get<6>(res)); auto rdma_send_flags_ = ConvertDetailTensorToPaddleTensor(std::get<7>(res)); const auto& event = std::get<8>(res); auto recv_hook = std::get<9>(res); return {packed_recv_x_, packed_recv_x_scales_, packed_recv_rdma_x_, packed_recv_count_, packed_rdma_recv_count_, packed_recv_src_info_, packed_recv_layout_range_, rdma_send_flags_, event, recv_hook}; #else LOG(ERROR) << "NVSHMEM is not enabled. You can enable it by setting cmake " "option WITH_NVSHMEM=ON."; return {}; #endif } std::tuple, std::optional>> Buffer::low_latency_combine_two_stage_api( const paddle::Tensor& x, const paddle::Tensor& rdma_recv_x, const paddle::Tensor& topk_idx, const paddle::Tensor& topk_weights, const paddle::Tensor& src_info, const paddle::Tensor& layout_range, const paddle::Tensor& rdma_send_flags, const paddle::Tensor& dispatch_rdma_recv_count, int num_max_dispatch_tokens_per_rank, int num_experts, bool dispatch_use_fp8, bool async, bool return_recv_hook, int num_per_channel, const std::optional& out) { #ifdef PADDLE_WITH_NVSHMEM const auto& x_ = ConvertPaddleTensorToDetailTensor(x); const auto& rdma_recv_x_ = ConvertPaddleTensorToDetailTensor(rdma_recv_x); const auto& topk_idx_ = ConvertPaddleTensorToDetailTensor(topk_idx); const auto& topk_weights_ = ConvertPaddleTensorToDetailTensor(topk_weights); const auto& src_info_ = ConvertPaddleTensorToDetailTensor(src_info); const auto& layout_range_ = ConvertPaddleTensorToDetailTensor(layout_range); const auto& rdma_send_flags_ = ConvertPaddleTensorToDetailTensor(rdma_send_flags); const auto& dispatch_rdma_recv_count_ = ConvertPaddleTensorToDetailTensor(dispatch_rdma_recv_count); std::optional out_ = std::nullopt; if (out.has_value()) { out_ = ConvertOptionalPaddleTensorToDetailTensor(out.value()); } auto res = low_latency_combine_two_stage(x_, rdma_recv_x_, topk_idx_, topk_weights_, src_info_, layout_range_, rdma_send_flags_, dispatch_rdma_recv_count_, num_max_dispatch_tokens_per_rank, num_experts, dispatch_use_fp8, async, return_recv_hook, num_per_channel, out_); auto combined_x_ = ConvertDetailTensorToPaddleTensor(std::get<0>(res)); const auto& event = std::get<1>(res); auto recv_hook = std::get<2>(res); return {combined_x_, event, recv_hook}; #else LOG(ERROR) << "NVSHMEM is not enabled. You can enable it by setting cmake " "option WITH_NVSHMEM=ON."; return {}; #endif } std::tuple, paddle::Tensor, paddle::Tensor, paddle::Tensor, paddle::Tensor, paddle::Tensor, paddle::Tensor, std::optional, std::optional>> Buffer::m2n_low_latency_dispatch_two_stage_api( const paddle::Tensor& x, const paddle::Tensor& topk_idx, const paddle::Tensor& topk_weights, int num_max_dispatch_tokens_per_rank, int num_experts, int a_start_rank, int a_num_ranks, int e_start_rank, int e_num_ranks, bool use_fp8, bool async, bool return_recv_hook) { #ifdef PADDLE_WITH_NVSHMEM const auto& x_ = ConvertPaddleTensorToDetailTensor(x); const auto& topk_idx_ = ConvertPaddleTensorToDetailTensor(topk_idx); const auto& topk_weights_ = ConvertPaddleTensorToDetailTensor(topk_weights); auto res = m2n_low_latency_dispatch_two_stage(x_, topk_idx_, topk_weights_, num_max_dispatch_tokens_per_rank, num_experts, a_start_rank, a_num_ranks, e_start_rank, e_num_ranks, use_fp8, async, return_recv_hook); auto packed_recv_x_ = ConvertDetailTensorToPaddleTensor(std::get<0>(res)); std::optional packed_recv_x_scales_; if (std::get<1>(res).has_value()) { packed_recv_x_scales_ = ConvertDetailTensorToPaddleTensor(std::get<1>(res).value()); } auto packed_recv_rdma_x_ = ConvertDetailTensorToPaddleTensor(std::get<2>(res)); auto packed_recv_count_ = ConvertDetailTensorToPaddleTensor(std::get<3>(res)); auto packed_rdma_recv_count_ = ConvertDetailTensorToPaddleTensor(std::get<4>(res)); auto packed_recv_src_info_ = ConvertDetailTensorToPaddleTensor(std::get<5>(res)); auto packed_recv_layout_range_ = ConvertDetailTensorToPaddleTensor(std::get<6>(res)); auto rdma_send_flags_ = ConvertDetailTensorToPaddleTensor(std::get<7>(res)); const auto& event = std::get<8>(res); auto recv_hook = std::get<9>(res); return {packed_recv_x_, packed_recv_x_scales_, packed_recv_rdma_x_, packed_recv_count_, packed_rdma_recv_count_, packed_recv_src_info_, packed_recv_layout_range_, rdma_send_flags_, event, recv_hook}; #else LOG(ERROR) << "NVSHMEM is not enabled. You can enable it by setting cmake " "option WITH_NVSHMEM=ON."; return {}; #endif } std::tuple, std::optional>> Buffer::m2n_low_latency_combine_two_stage_api( const paddle::Tensor& x, const paddle::Tensor& rdma_recv_x, const paddle::Tensor& topk_idx, const paddle::Tensor& topk_weights, const paddle::Tensor& src_info, const paddle::Tensor& layout_range, const paddle::Tensor& rdma_send_flags, const paddle::Tensor& dispatch_rdma_recv_count, int num_max_dispatch_tokens_per_rank, int num_experts, int a_start_rank, int a_num_ranks, int e_start_rank, int e_num_ranks, bool dispatch_use_fp8, bool async, bool return_recv_hook, const std::optional& out) { #ifdef PADDLE_WITH_NVSHMEM const auto& x_ = ConvertPaddleTensorToDetailTensor(x); const auto& rdma_recv_x_ = ConvertPaddleTensorToDetailTensor(rdma_recv_x); const auto& topk_idx_ = ConvertPaddleTensorToDetailTensor(topk_idx); const auto& topk_weights_ = ConvertPaddleTensorToDetailTensor(topk_weights); const auto& src_info_ = ConvertPaddleTensorToDetailTensor(src_info); const auto& layout_range_ = ConvertPaddleTensorToDetailTensor(layout_range); const auto& rdma_send_flags_ = ConvertPaddleTensorToDetailTensor(rdma_send_flags); const auto& dispatch_rdma_recv_count_ = ConvertPaddleTensorToDetailTensor(dispatch_rdma_recv_count); std::optional out_ = std::nullopt; if (out.has_value()) { out_ = ConvertOptionalPaddleTensorToDetailTensor(out.value()); } auto res = m2n_low_latency_combine_two_stage(x_, rdma_recv_x_, topk_idx_, topk_weights_, src_info_, layout_range_, rdma_send_flags_, dispatch_rdma_recv_count_, num_max_dispatch_tokens_per_rank, num_experts, a_start_rank, a_num_ranks, e_start_rank, e_num_ranks, dispatch_use_fp8, async, return_recv_hook, out_); auto combined_x_ = ConvertDetailTensorToPaddleTensor(std::get<0>(res)); const auto& event = std::get<1>(res); auto recv_hook = std::get<2>(res); return {combined_x_, event, recv_hook}; #else LOG(ERROR) << "NVSHMEM is not enabled. You can enable it by setting cmake " "option WITH_NVSHMEM=ON."; return {}; #endif } std::tuple, paddle::Tensor, paddle::Tensor, std::optional> Buffer::get_dispatch_layout_api(const paddle::Tensor& topk_idx, int num_experts, std::optional& previous_event, bool async, bool allocate_on_comm_stream) { const auto& topk_idx_ = ConvertPaddleTensorToDetailTensor(topk_idx); auto res = get_dispatch_layout( topk_idx_, num_experts, previous_event, async, allocate_on_comm_stream); const auto& num_tokens_per_rank = std::get<0>(res); const auto& num_tokens_per_rdma_rank = std::get<1>(res); const auto& num_tokens_per_expert = std::get<2>(res); const auto& is_token_in_rank = std::get<3>(res); const auto& event = std::get<4>(res); auto num_tokens_per_rank_ = ConvertDetailTensorToPaddleTensor(num_tokens_per_rank); std::optional num_tokens_per_rdma_rank_ = std::nullopt; if (num_tokens_per_rdma_rank.has_value()) { num_tokens_per_rdma_rank_ = ConvertDetailTensorToPaddleTensor(num_tokens_per_rdma_rank.value()); } auto num_tokens_per_expert_ = ConvertDetailTensorToPaddleTensor(num_tokens_per_expert); auto is_token_in_rank_ = ConvertDetailTensorToPaddleTensor(is_token_in_rank); return {num_tokens_per_rank_, num_tokens_per_rdma_rank_, num_tokens_per_expert_, is_token_in_rank_, event}; } std::tuple, std::optional, std::optional, std::vector, paddle::Tensor, paddle::Tensor, paddle::Tensor, paddle::Tensor, paddle::Tensor, std::optional> Buffer::intranode_dispatch_api( const paddle::Tensor& x, const std::optional& x_scales, const std::optional& topk_idx, const std::optional& topk_weights, const std::optional& num_tokens_per_rank, const paddle::Tensor& is_token_in_rank, const std::optional& num_tokens_per_expert, int cached_num_recv_tokens, const std::optional& cached_rank_prefix_matrix, const std::optional& cached_channel_prefix_matrix, int expert_alignment, const Config& config, std::optional& previous_event, // NOLINT bool async, bool allocate_on_comm_stream) { const auto& x_ = ConvertPaddleTensorToDetailTensor(x); std::optional x_scales_; if (x_scales.has_value()) { x_scales_ = ConvertPaddleTensorToDetailTensor(x_scales.value()); } std::optional topk_idx_; if (topk_idx.has_value()) { topk_idx_ = ConvertPaddleTensorToDetailTensor(topk_idx.value()); } std::optional topk_weights_; if (topk_weights.has_value()) { topk_weights_ = ConvertPaddleTensorToDetailTensor(topk_weights.value()); } std::optional num_tokens_per_rank_; if (num_tokens_per_rank.has_value()) { num_tokens_per_rank_ = ConvertPaddleTensorToDetailTensor(num_tokens_per_rank.value()); } const auto& is_token_in_rank_ = ConvertPaddleTensorToDetailTensor(is_token_in_rank); std::optional num_tokens_per_expert_; if (num_tokens_per_expert.has_value()) { num_tokens_per_expert_ = ConvertPaddleTensorToDetailTensor(num_tokens_per_expert.value()); } std::optional cached_rank_prefix_matrix_; if (cached_rank_prefix_matrix.has_value()) { cached_rank_prefix_matrix_ = ConvertPaddleTensorToDetailTensor(cached_rank_prefix_matrix.value()); } std::optional cached_channel_prefix_matrix_; if (cached_channel_prefix_matrix.has_value()) { cached_channel_prefix_matrix_ = ConvertPaddleTensorToDetailTensor(cached_channel_prefix_matrix.value()); } auto res = intranode_dispatch(x_, x_scales_, topk_idx_, topk_weights_, num_tokens_per_rank_, is_token_in_rank_, num_tokens_per_expert_, cached_num_recv_tokens, cached_rank_prefix_matrix_, cached_channel_prefix_matrix_, expert_alignment, config, previous_event, async, allocate_on_comm_stream); const auto& recv_x = std::get<0>(res); const auto& recv_x_scales = std::get<1>(res); const auto& recv_topk_idx = std::get<2>(res); const auto& recv_topk_weights = std::get<3>(res); const auto& num_recv_tokens_per_expert_list = std::get<4>(res); const auto& rank_prefix_matrix = std::get<5>(res); const auto& channel_prefix_matrix = std::get<6>(res); const auto& recv_channel_prefix_matrix = std::get<7>(res); const auto& recv_src_idx = std::get<8>(res); const auto& send_head = std::get<9>(res); const auto& event = std::get<10>(res); auto recv_x_ = ConvertDetailTensorToPaddleTensor(recv_x); std::optional recv_x_scales_; if (recv_x_scales.has_value()) { recv_x_scales_ = ConvertDetailTensorToPaddleTensor(recv_x_scales.value()); } std::optional recv_topk_idx_; if (recv_topk_idx.has_value()) { recv_topk_idx_ = ConvertDetailTensorToPaddleTensor(recv_topk_idx.value()); } std::optional recv_topk_weights_; if (recv_topk_weights.has_value()) { recv_topk_weights_ = ConvertDetailTensorToPaddleTensor(recv_topk_weights.value()); } auto rank_prefix_matrix_ = ConvertDetailTensorToPaddleTensor(rank_prefix_matrix); auto channel_prefix_matrix_ = ConvertDetailTensorToPaddleTensor(channel_prefix_matrix); auto recv_channel_prefix_matrix_ = ConvertDetailTensorToPaddleTensor(recv_channel_prefix_matrix); auto recv_src_idx_ = ConvertDetailTensorToPaddleTensor(recv_src_idx); auto send_head_ = ConvertDetailTensorToPaddleTensor(send_head); return {recv_x_, recv_x_scales_, recv_topk_idx_, recv_topk_weights_, num_recv_tokens_per_expert_list, rank_prefix_matrix_, channel_prefix_matrix_, recv_channel_prefix_matrix_, recv_src_idx_, send_head_, event}; } std::tuple, std::optional> Buffer::intranode_combine_api(const paddle::Tensor& x, const std::optional& topk_weights, const paddle::Tensor& src_idx, const paddle::Tensor& rank_prefix_matrix, const paddle::Tensor& channel_prefix_matrix, const paddle::Tensor& send_head, const Config& config, std::optional& previous_event, bool async, bool allocate_on_comm_stream) { const auto& x_ = ConvertPaddleTensorToDetailTensor(x); std::optional topk_weights_; if (topk_weights.has_value()) { topk_weights_ = ConvertPaddleTensorToDetailTensor(topk_weights.value()); } const auto& src_idx_ = ConvertPaddleTensorToDetailTensor(src_idx); const auto& rank_prefix_matrix_ = ConvertPaddleTensorToDetailTensor(rank_prefix_matrix); const auto& channel_prefix_matrix_ = ConvertPaddleTensorToDetailTensor(channel_prefix_matrix); const auto& send_head_ = ConvertPaddleTensorToDetailTensor(send_head); auto res = intranode_combine(x_, topk_weights_, src_idx_, rank_prefix_matrix_, channel_prefix_matrix_, send_head_, config, previous_event, async, allocate_on_comm_stream); const auto& recv_x = std::get<0>(res); const auto& recv_topk_weights = std::get<1>(res); const auto& event = std::get<2>(res); auto recv_x_ = ConvertDetailTensorToPaddleTensor(recv_x); std::optional recv_topk_weights_; if (recv_topk_weights.has_value()) { recv_topk_weights_ = ConvertDetailTensorToPaddleTensor(recv_topk_weights.value()); } auto event_ = event; return {recv_x_, recv_topk_weights_, event_}; } deep_ep::detail::Tensor ConvertPaddleTensorToDetailTensor( const paddle::Tensor& tensor) { deep_ep::detail::Tensor res(tensor); return res; } paddle::Tensor ConvertDetailTensorToPaddleTensor( const deep_ep::detail::Tensor& tensor) { return tensor.raw_tensor(); } std::optional ConvertOptionalPaddleTensorToDetailTensor( const std::optional& tensor) { std::optional res; if (tensor.has_value()) { res = ConvertPaddleTensorToDetailTensor(tensor.value()); } return res; } std::optional ConvertOptionalDetailTensorToPaddleTensor( const std::optional& tensor) { std::optional res; if (tensor.has_value()) { res = ConvertDetailTensorToPaddleTensor(tensor.value()); } return res; } } // namespace deep_ep