/* * SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. * SPDX-License-Identifier: Apache-2.0 * See LICENSE.txt for more license information. */ #include #include #include "cuda_runtime.h" #include "nccl.h" #include "nccl_ep.h" #include "mpi.h" #include #include #include #include // Custom allocator wrappers (can be replaced with custom memory pool) static cudaError_t torchMalloc(void** ptr, size_t size) { return cudaMalloc(ptr, size); } static cudaError_t torchFree(void* ptr) { return cudaFree(ptr); } #define MPICHECK(cmd) do { \ int e = cmd; \ if( e != MPI_SUCCESS ) { \ printf("Failed: MPI error %s:%d '%d'\n", \ __FILE__,__LINE__, e); \ exit(EXIT_FAILURE); \ } \ } while(0) #define CUDACHECK(cmd) do { \ cudaError_t e = cmd; \ if( e != cudaSuccess ) { \ printf("Failed: Cuda error %s:%d '%s'\n", \ __FILE__,__LINE__,cudaGetErrorString(e)); \ exit(EXIT_FAILURE); \ } \ } while(0) #define NCCLCHECK(cmd) do { \ ncclResult_t r = cmd; \ if (r!= ncclSuccess) { \ printf("Failed: NCCL error %s:%d '%s'\n", \ __FILE__,__LINE__,ncclGetErrorString(r)); \ exit(EXIT_FAILURE); \ } \ } while(0) static uint64_t getHostHash(const char* string) { // Based on DJB2, result = result * 33 + char uint64_t result = 5381; for (int c = 0; string[c] != '\0'; c++) { result = ((result << 5) + result) + string[c]; } return result; } // Convert float to bfloat16 (as uint16_t) static inline uint16_t float_to_bf16(float f) { uint32_t x = *reinterpret_cast(&f); // Round to nearest even uint32_t rounding_bias = 0x00007FFF + ((x >> 16) & 1); return static_cast((x + rounding_bias) >> 16); } static void getHostName(char* hostname, int maxlen) { gethostname(hostname, maxlen); for (int i=0; i< maxlen; i++) { if (hostname[i] == '.') { hostname[i] = '\0'; return; } } } void printUsage(const char* programName, int myRank) { if (myRank == 0) { printf("Usage: %s [OPTIONS]\n", programName); printf("Options:\n"); printf(" -a Set algorithm mode (default: ll)\n"); printf(" ll: Low latency mode (NCCL_EP_ALGO_LOW_LATENCY)\n"); printf(" ht: High throughput mode (NCCL_EP_ALGO_HIGH_THROUGHPUT)\n"); printf(" -m Disable max_tokens_per_rank (only supported with HT mode)\n"); printf(" -s Set send_only mode (default: none)\n"); printf(" none: send_only=false for both dispatch and combine\n"); printf(" dispatch: send_only=true for dispatch only\n"); printf(" combine: send_only=true for combine only\n"); printf(" both: send_only=true for both dispatch and combine\n"); printf(" -c Enable cached mode (default: disabled, only supported for HT)\n"); printf(" -r Enable random mode (random topk_idx, skip correctness checks)\n"); printf(" -t Set number of tokens (default: 50)\n"); printf(" -d Set hidden dimension size (default: 7168)\n"); printf(" -h Show this help message\n"); } } int main(int argc, char* argv[]) { int myRank, nRanks, localRank = 0; ncclEpAlgorithm_t algorithm = NCCL_EP_ALGO_LOW_LATENCY; // Default to 'll' (low latency) bool disable_max_tokens = false; // Flag to disable max_tokens_per_rank bool dispatch_send_only = false; // send_only flag for dispatch bool combine_send_only = false; // send_only flag for combine bool cached_mode = false; // cached mode flag bool random_mode = false; // random mode flag (random topk_idx, skip checks) unsigned int num_tokens = 50; // number of tokens (default) unsigned int hidden = 7168; // hidden dimension size (default) // initializing MPI MPICHECK(MPI_Init(&argc, &argv)); MPICHECK(MPI_Comm_rank(MPI_COMM_WORLD, &myRank)); MPICHECK(MPI_Comm_size(MPI_COMM_WORLD, &nRanks)); if (nRanks != 2 && nRanks % 4 != 0) { printf("Error: nRanks must be 2 or a multiple of 4 for this test\n"); MPI_Finalize(); exit(EXIT_FAILURE); } // Parse command line arguments using getopt int opt; while ((opt = getopt(argc, argv, "a:ms:crt:d:h")) != -1) { switch (opt) { case 'a': if (strcmp(optarg, "ll") == 0) { algorithm = NCCL_EP_ALGO_LOW_LATENCY; } else if (strcmp(optarg, "ht") == 0) { algorithm = NCCL_EP_ALGO_HIGH_THROUGHPUT; } else { if (myRank == 0) { printf("Error: Invalid algorithm '%s'. Use 'll' or 'ht'\n", optarg); printUsage(argv[0], myRank); } MPI_Finalize(); exit(EXIT_FAILURE); } break; case 'm': disable_max_tokens = true; break; case 's': if (strcmp(optarg, "none") == 0) { dispatch_send_only = false; combine_send_only = false; } else if (strcmp(optarg, "dispatch") == 0) { dispatch_send_only = true; combine_send_only = false; } else if (strcmp(optarg, "combine") == 0) { dispatch_send_only = false; combine_send_only = true; } else if (strcmp(optarg, "both") == 0) { dispatch_send_only = true; combine_send_only = true; } else { if (myRank == 0) { printf("Error: Invalid send_only mode '%s'. Use 'none', 'dispatch', 'combine', or 'both'\n", optarg); printUsage(argv[0], myRank); } MPI_Finalize(); exit(EXIT_FAILURE); } break; case 'c': cached_mode = true; break; case 'r': random_mode = true; break; case 't': num_tokens = static_cast(atoi(optarg)); if (num_tokens == 0) { if (myRank == 0) { printf("Error: Invalid num_tokens '%s'. Must be a positive integer.\n", optarg); printUsage(argv[0], myRank); } MPI_Finalize(); exit(EXIT_FAILURE); } break; case 'd': hidden = static_cast(atoi(optarg)); if (hidden == 0) { if (myRank == 0) { printf("Error: Invalid hidden size '%s'. Must be a positive integer.\n", optarg); printUsage(argv[0], myRank); } MPI_Finalize(); exit(EXIT_FAILURE); } break; case 'h': printUsage(argv[0], myRank); MPI_Finalize(); exit(EXIT_SUCCESS); case '?': if (myRank == 0) { printUsage(argv[0], myRank); } MPI_Finalize(); exit(EXIT_FAILURE); default: if (myRank == 0) { printUsage(argv[0], myRank); } MPI_Finalize(); exit(EXIT_FAILURE); } } // -m (NCCL_EP_AUTO for max_tokens_per_rank) is intended for HT mode only. // Not yet supported in the current release; code paths are kept for future use. if (disable_max_tokens) { if (myRank == 0) { if (algorithm != NCCL_EP_ALGO_HIGH_THROUGHPUT) printf("Error: -m is only applicable to HT mode (-a ht)\n"); else printf("Error: -m (NCCL_EP_AUTO for max_tokens_per_rank) is not yet supported.\n" " This feature will be available in a future release for HT mode.\n"); } MPI_Finalize(); exit(EXIT_FAILURE); } // calculating localRank based on hostname which is used in selecting a GPU uint64_t hostHashs[nRanks]; const unsigned int ELEMENTS_TESTED_PER_TOKEN = 10; unsigned int top_k = std::min(8, nRanks); // DeepSeek v3 has 8, how many experts each token goes to unsigned int num_experts = std::min(256u, top_k * nRanks); // DeepSeek v3 unsigned int num_local_experts = num_experts / nRanks; unsigned int local_experts_start = num_local_experts * myRank; unsigned int local_experts_end = local_experts_start + num_local_experts; // exclusive if (num_experts % nRanks != 0) { if (myRank == 0) printf("Error: num_experts (%u) must be divisible by nRanks (%d)\n", num_experts, nRanks); MPI_Finalize(); exit(EXIT_FAILURE); } if (top_k > num_local_experts) { if (myRank == 0) printf("Error: top_k (%u) must be less than or equal to num_local_experts (%u)\n", top_k, num_local_experts); MPI_Finalize(); exit(EXIT_FAILURE); } char hostname[1024]; getHostName(hostname, 1024); hostHashs[myRank] = getHostHash(hostname); MPICHECK(MPI_Allgather(MPI_IN_PLACE, 0, MPI_DATATYPE_NULL, hostHashs, sizeof(uint64_t), MPI_BYTE, MPI_COMM_WORLD)); for (int p = 0; p < nRanks; p++) { if (p == myRank) break; if (hostHashs[p] == hostHashs[myRank]) localRank++; } ncclUniqueId id; ncclComm_t comm; cudaStream_t s; // get NCCL unique ID at rank 0 and broadcast it to all others if (myRank == 0) ncclGetUniqueId(&id); MPICHECK(MPI_Bcast(static_cast(&id), sizeof(id), MPI_BYTE, 0, MPI_COMM_WORLD)); // picking a GPU based on localRank, allocate device buffers CUDACHECK(cudaSetDevice(localRank)); CUDACHECK(cudaStreamCreate(&s)); // initializing NCCL NCCLCHECK(ncclCommInitRank(&comm, nRanks, id, myRank)); ncclEpGroup_t ep_group; ncclEpGroupConfig_t config; config.version = 1; // Structure version config.algorithm = algorithm; // Algorithm type (set by command line) config.num_experts = num_experts; // max_tokens_per_rank is the per-rank batch size (max tokens any single rank will send). config.max_tokens_per_rank = disable_max_tokens ? NCCL_EP_AUTO : num_tokens; config.token_size_bytes = hidden * 2; // bfloat16 config.rdma_buffer_size = NCCL_EP_AUTO; // NCCL_EP_AUTO for auto configuration, internally uses the hint config.num_qp_per_rank = NCCL_EP_AUTO; // Default is 24, see internode_ll.cu:181 for the minimum config.num_channels = NCCL_EP_AUTO; // Number of communication channels const char* algorithm_name = (algorithm == NCCL_EP_ALGO_LOW_LATENCY) ? "LOW_LATENCY" : "HIGH_THROUGHPUT"; printf("Rank %d: Testing ncclEpCreateGroup with algorithm: %s%s\n", myRank, algorithm_name, disable_max_tokens ? " (no max_tokens_per_rank)" : ""); NCCLCHECK(ncclEpCreateGroup(&ep_group, comm, &config, s, torchMalloc, torchFree)); ncclNDTensor_t topk_idx; NCCLCHECK(ncclEpTensorCreate(ep_group, &topk_idx, 2, ncclInt64, NCCL_EP_TENSOR_TAG_TOPK_IDX, nullptr, static_cast(num_tokens), static_cast(top_k))); int64_t *topk_idx_host = new int64_t[num_tokens * top_k]; if (random_mode) { // Random mode: first expert is random, rest are deterministic to avoid repetitions srand(myRank + 42); // Seed with rank for reproducibility for (int i = 0; i < num_tokens; i++) { // First expert is random int64_t first_expert = rand() % num_experts; topk_idx_host[i * top_k + 0] = first_expert; // Remaining experts are deterministic based on first choice (no repetitions) for (int j = 1; j < top_k; j++) { topk_idx_host[i * top_k + j] = (first_expert + j) % num_experts; } } if (myRank == 0) { printf("Random mode enabled: first expert random, rest deterministic (no repetitions)\n"); } } else { // Deterministic mode: send each token to top_k number of semi-random experts, equal distribution for (int i = 0; i < num_tokens; i++) { for (int j = 0; j < top_k; j++) { topk_idx_host[i * top_k + j] = (local_experts_end + j) % num_experts; } } } void* topk_idx_data; NCCLCHECK(ncclEpTensorGetData(topk_idx, &topk_idx_data)); CUDACHECK(cudaMemcpy(topk_idx_data, topk_idx_host, num_tokens * top_k * sizeof(int64_t), cudaMemcpyHostToDevice)); // Create recv_expert_counter host tensor for ncclEpCreateHandle (only when disable_max_tokens is true) ncclNDTensor_t handle_local_tensors[1] = {nullptr}; unsigned int handle_num_local_tensors = 0; ncclNDTensor_t handle_recv_expert_counter = nullptr; if (disable_max_tokens) { void* recv_counter_data; CUDACHECK(cudaHostAlloc(&recv_counter_data, num_local_experts * sizeof(int), cudaHostAllocMapped)); NCCLCHECK(ncclEpTensorCreate(ep_group, &handle_recv_expert_counter, 1, ncclInt32, NCCL_EP_TENSOR_TAG_RECV_EXPERT_COUNTER_HOST, recv_counter_data, num_local_experts)); handle_local_tensors[0] = handle_recv_expert_counter; handle_num_local_tensors = 1; } printf("Rank %d: Testing ncclEpCreateHandle\n", myRank); ncclEpHandle_t ep_handle; NCCLCHECK(ncclEpCreateHandle(&ep_handle, ep_group, topk_idx, handle_local_tensors, handle_num_local_tensors, nullptr, s)); CUDACHECK(cudaStreamSynchronize(s)); unsigned int num_recv_tokens = 0; if (disable_max_tokens) { NCCLCHECK(ncclEpHandleGetNumRecvTokens(ep_handle, &num_recv_tokens)); } else { // Specific to this test num_recv_tokens = config.max_tokens_per_rank * num_local_experts; } assert(num_recv_tokens); ncclEpDispatchConfig_t dispatch_config; dispatch_config.round_scales = 0; // Not testing this parameter atm // Array sizes depend on algorithm: LL uses 1 input/output, HT use 3 inputs/3 outputs int num_inputs = (algorithm == NCCL_EP_ALGO_LOW_LATENCY) ? 1 : 3; int num_outputs = (algorithm == NCCL_EP_ALGO_LOW_LATENCY) ? 1 : 3; int num_local_tensors = (algorithm == NCCL_EP_ALGO_LOW_LATENCY) ? 1 : 0; ncclNDTensor_t inputs[3]; ncclNDTensor_t outputs[3]; ncclNDTensor_t local_tensors[1]; NCCLCHECK(ncclEpTensorCreate(ep_group, &inputs[0], 2, ncclBfloat16, NCCL_EP_TENSOR_TAG_TOKENS, nullptr, static_cast(num_tokens), static_cast(hidden))); ncclNDTensor_t topk_weights; if (algorithm != NCCL_EP_ALGO_LOW_LATENCY) { // HT: topk_weights and topk_idx are dispatch inputs NCCLCHECK(ncclEpTensorCreate(ep_group, &topk_weights, 2, ncclFloat32, NCCL_EP_TENSOR_TAG_TOPK_WEIGHTS, nullptr, static_cast(num_tokens), static_cast(top_k))); inputs[1] = topk_weights; inputs[2] = topk_idx; } else { NCCLCHECK(ncclEpTensorCreate(ep_group, &topk_weights, 2, ncclFloat32, NCCL_EP_TENSOR_TAG_TOPK_WEIGHTS, nullptr, static_cast(num_tokens), static_cast(top_k))); } // outputs[0] shape depends on algorithm if (algorithm == NCCL_EP_ALGO_LOW_LATENCY) { // LL mode: outputs[0] is [num_local_experts, max_recv_tokens * nRanks, hidden] NCCLCHECK(ncclEpTensorCreate(ep_group, &outputs[0], 3, ncclBfloat16, NCCL_EP_TENSOR_TAG_TOKENS, nullptr, static_cast(num_local_experts), config.max_tokens_per_rank * nRanks, static_cast(hidden))); } else { // HT mode: outputs[0] is [num_tokens, hidden] NCCLCHECK(ncclEpTensorCreate(ep_group, &outputs[0], 2, ncclBfloat16, NCCL_EP_TENSOR_TAG_TOKENS, nullptr, static_cast(num_recv_tokens), static_cast(hidden))); } if (algorithm == NCCL_EP_ALGO_LOW_LATENCY) { NCCLCHECK(ncclEpTensorCreate(ep_group, &local_tensors[0], 1, ncclInt32, NCCL_EP_TENSOR_TAG_RECV_EXPERT_COUNTER_DEVICE, nullptr, static_cast(num_local_experts))); } // In HT mode, outputs[1] (recv_topk_weights) and outputs[2] (recv_topk_idx) are 2D tensors [num_recv_tokens, top_k] if (algorithm != NCCL_EP_ALGO_LOW_LATENCY) { NCCLCHECK(ncclEpTensorCreate(ep_group, &outputs[1], 2, ncclFloat32, NCCL_EP_TENSOR_TAG_TOPK_WEIGHTS, nullptr, static_cast(num_recv_tokens), static_cast(top_k))); NCCLCHECK(ncclEpTensorCreate(ep_group, &outputs[2], 2, ncclInt64, NCCL_EP_TENSOR_TAG_TOPK_IDX, nullptr, static_cast(num_recv_tokens), static_cast(top_k))); } // Fill the first ELEMENTS_TESTED_PER_TOKEN elements of each token with a special value based on the current rank uint16_t *input_host = new uint16_t[num_tokens * hidden](); for (int i = 0; i < num_tokens; ++i) { for (int j = 0; j < ELEMENTS_TESTED_PER_TOKEN; ++j) { input_host[i * hidden + j] = static_cast(0x1000 + myRank); } } void* input0_data; NCCLCHECK(ncclEpTensorGetData(inputs[0], &input0_data)); CUDACHECK(cudaMemcpy(input0_data, input_host, num_tokens * hidden * 2, cudaMemcpyHostToDevice)); delete[] input_host; // Create topk_weights (used as input for HT mode, local tensor for LL mode) float *topk_weights_host = new float[num_tokens * top_k]; for (int i = 0; i < num_tokens; ++i) { for (int j = 0; j < top_k; ++j) { topk_weights_host[i * top_k + j] = 1.0f / top_k; // Equal weights } } void* topk_weights_data; NCCLCHECK(ncclEpTensorGetData(topk_weights, &topk_weights_data)); CUDACHECK(cudaMemcpy(topk_weights_data, topk_weights_host, num_tokens * top_k * sizeof(float), cudaMemcpyHostToDevice)); delete[] topk_weights_host; // Host buffer for first phase dispatch output (kept for cached mode comparison in HT mode) uint16_t *first_dispatch_output0_host = nullptr; printf("Rank %d: Testing ncclEpDispatch (send_only=%s)\n", myRank, dispatch_send_only ? "true" : "false"); NCCLCHECK(ncclEpDispatch(ep_handle, inputs, num_inputs, outputs, num_outputs, local_tensors, num_local_tensors, dispatch_send_only /* send_only */, &dispatch_config, s)); printf("Rank %d: Testing ncclEpComplete\n", myRank); NCCLCHECK(ncclEpComplete(ep_handle, nullptr, s)); CUDACHECK(cudaStreamSynchronize(s)); // Read recv_count tensor to use for validation // LL mode: allocated and copied from device local_tensors[0] // HT mode with disable_max_tokens: points to handle_local_tensors[0] (already host memory) // HT mode without disable_max_tokens: nullptr (no validation available) int *recv_count_host = nullptr; bool should_free_recv_count = false; if (algorithm == NCCL_EP_ALGO_LOW_LATENCY) { recv_count_host = new int[num_local_experts]; void* local_tensor0_data; NCCLCHECK(ncclEpTensorGetData(local_tensors[0], &local_tensor0_data)); CUDACHECK(cudaMemcpy(recv_count_host, local_tensor0_data, num_local_experts * sizeof(int), cudaMemcpyDeviceToHost)); should_free_recv_count = true; } else if (disable_max_tokens && handle_local_tensors[0] != nullptr) { void* handle_local_tensor0_data; NCCLCHECK(ncclEpTensorGetData(handle_local_tensors[0], &handle_local_tensor0_data)); recv_count_host = static_cast(handle_local_tensor0_data); } unsigned int recv_from_expert_start = (local_experts_start + num_experts - num_local_experts) % num_experts; unsigned int recv_rank = recv_from_expert_start / num_local_experts; // Check the first ELEMENTS_TESTED_PER_TOKEN elements of each token // For LL mode: use recv_count to validate per-expert token counts // For HT mode: use the simple approach bool dispatch_check_passed = true; if (!random_mode && algorithm == NCCL_EP_ALGO_LOW_LATENCY && recv_count_host != nullptr) { // LL mode: outputs[0] is [num_local_experts, config.max_tokens_per_rank * nRanks, hidden] uint16_t *output_host = new uint16_t[num_local_experts * config.max_tokens_per_rank * nRanks * hidden](); void* output0_data; NCCLCHECK(ncclEpTensorGetData(outputs[0], &output0_data)); CUDACHECK(cudaMemcpy(output_host, output0_data, num_local_experts * config.max_tokens_per_rank * nRanks * hidden * 2, cudaMemcpyDeviceToHost)); for (unsigned int e = 0; e < num_local_experts; e++) { int expected_count = num_tokens; // Each expert receives num_tokens tokens (one from each rank) if (recv_count_host[e] != expected_count) { printf("Recv_count check failed! Rank %d, expert %d: expected %d, got %d\n", myRank, e, expected_count, recv_count_host[e]); dispatch_check_passed = false; break; } // Verify the first recv_count_host[e] tokens for this expert for (int t = 0; t < recv_count_host[e] && t < static_cast(config.max_tokens_per_rank * nRanks); t++) { size_t token_offset = (e * config.max_tokens_per_rank * nRanks + t) * hidden; for (int j = 0; j < ELEMENTS_TESTED_PER_TOKEN; ++j) { uint16_t expected = static_cast(0x1000 + recv_rank); uint16_t actual = output_host[token_offset + j]; if (actual != expected) { printf("Dispatch data check failed! Rank %d, expert %d, token %d, element %d: expected %d, got %d\n", myRank, e, t, j, expected, actual); dispatch_check_passed = false; break; } } if (!dispatch_check_passed) break; } if (!dispatch_check_passed) break; } delete[] output_host; } else if (!random_mode) { // HT mode or fallback (only check if not in random mode) // Check recv_count (only if available) if (recv_count_host != nullptr) { for (unsigned int e = 0; e < num_local_experts; e++) { int expected_count = num_tokens; // Each expert receives num_tokens tokens (one from each rank) if (recv_count_host[e] != expected_count) { printf("Recv_count check failed! Rank %d, expert %d: expected %d, got %d\n", myRank, e, expected_count, recv_count_host[e]); dispatch_check_passed = false; break; } } } uint16_t *output_host = new uint16_t[num_recv_tokens * hidden](); void* output0_data; NCCLCHECK(ncclEpTensorGetData(outputs[0], &output0_data)); CUDACHECK(cudaMemcpy(output_host, output0_data, num_recv_tokens * hidden * 2, cudaMemcpyDeviceToHost)); int expected_count = disable_max_tokens ? num_recv_tokens : num_tokens; for (int i = 0; i < expected_count; ++i) { for (int j = 0; j < ELEMENTS_TESTED_PER_TOKEN; ++j) { uint16_t expected = static_cast(0x1000 + recv_rank); uint16_t actual = output_host[i * hidden + j]; if (actual != expected) { printf("Dispatch check failed! Rank %d, token %d, element %d: expected %d, got %d\n", myRank, i, j, expected, actual); dispatch_check_passed = false; break; } } if (!dispatch_check_passed) break; } // Keep output_host for cached mode comparison, delete later first_dispatch_output0_host = output_host; } else { // Random mode: skip checks, just clean up } // Consolidated cleanup for recv_count_host if (should_free_recv_count && recv_count_host != nullptr) { delete[] recv_count_host; } // In HT mode, copy and verify outputs[1] (recv_topk_weights) and outputs[2] (recv_topk_idx) if (!random_mode && algorithm != NCCL_EP_ALGO_LOW_LATENCY) { // Copy recv_topk_weights (outputs[1]) - [num_recv_tokens, top_k] float32 float *recv_topk_weights_host = new float[num_recv_tokens * top_k](); void* output1_data; NCCLCHECK(ncclEpTensorGetData(outputs[1], &output1_data)); CUDACHECK(cudaMemcpy(recv_topk_weights_host, output1_data, num_recv_tokens * top_k * sizeof(float), cudaMemcpyDeviceToHost)); // Copy recv_topk_idx (outputs[2]) - [num_recv_tokens, top_k] int64 int64_t *recv_topk_idx_host = new int64_t[num_recv_tokens * top_k](); void* output2_data; NCCLCHECK(ncclEpTensorGetData(outputs[2], &output2_data)); CUDACHECK(cudaMemcpy(recv_topk_idx_host, output2_data, num_recv_tokens * top_k * sizeof(int64_t), cudaMemcpyDeviceToHost)); // Verify recv_topk_weights and recv_topk_idx bool ht_outputs_valid = true; printf("Rank %d: Verifying recv_topk_weights and recv_topk_idx\n", myRank); // Test ELEMENTS_TESTED_PER_TOKEN (or top_k if smaller) per token float expected_weight = 1.0f / top_k; int weight_errors = 0; int idx_errors = 0; for (int i = 0; i < num_tokens; i++) { for (int j = 0; j < top_k; j++) { int offset = i * top_k + j; // Check that recv_topk_weights equals 1.0f / top_k if (recv_topk_weights_host[offset] != expected_weight) { if (weight_errors < 5) { // Limit error output printf("Rank %d: recv_topk_weights[%d][%d] = %f, expected %f\n", myRank, i, j, recv_topk_weights_host[offset], expected_weight); } weight_errors++; ht_outputs_valid = false; } // Check that recv_topk_idx is within valid range [0, num_experts) int64_t idx_val = recv_topk_idx_host[offset]; if (idx_val < 0 || idx_val >= static_cast(num_experts)) { if (idx_errors < 5) { // Limit error output printf("Rank %d: recv_topk_idx[%d][%d] = %ld, expected range [0, %u)\n", myRank, i, j, static_cast(idx_val), num_experts); } idx_errors++; ht_outputs_valid = false; } } } if (weight_errors > 0) { printf("Rank %d: recv_topk_weights verification failed with %d errors\n", myRank, weight_errors); } if (idx_errors > 0) { printf("Rank %d: recv_topk_idx verification failed with %d errors\n", myRank, idx_errors); } if (ht_outputs_valid) { printf("Rank %d: %s mode recv_topk_weights and recv_topk_idx verification passed\n", myRank, algorithm_name); } else { dispatch_check_passed = false; } delete[] recv_topk_weights_host; delete[] recv_topk_idx_host; } if (random_mode) { printf("Rank %d: %s Dispatch flow completed (random mode, checks skipped)\n", myRank, algorithm_name); } else if (dispatch_check_passed) { printf("Rank %d: %s Dispatch flow passed successfully\n", myRank, algorithm_name); } else { printf("Rank %d: Exiting test due to dispatch failure\n", myRank); exit(EXIT_FAILURE); } printf("Rank %d: Testing %s Combine flow\n", myRank, algorithm_name); // Create tensors for combine ncclNDTensor_t expert_outputs; if (algorithm == NCCL_EP_ALGO_LOW_LATENCY) { // LL mode: expert_outputs is 3D [num_local_experts, config.max_tokens_per_rank * nRanks, hidden] NCCLCHECK(ncclEpTensorCreate(ep_group, &expert_outputs, 3, ncclBfloat16, NCCL_EP_TENSOR_TAG_TOKENS, nullptr, static_cast(num_local_experts), static_cast(config.max_tokens_per_rank * nRanks), static_cast(hidden))); // Fill expert outputs with multiple test values per token // The ith element will be (i+1)*2 for all experts uint16_t *expert_outputs_host = new uint16_t[config.max_tokens_per_rank * hidden](); for (unsigned int t = 0; t < config.max_tokens_per_rank; ++t) { for (int j = 0; j < ELEMENTS_TESTED_PER_TOKEN; ++j) { // Set the ith element to (i+1)*2, stored as bf16 expert_outputs_host[t * hidden + j] = float_to_bf16(static_cast((j + 1) * 2)); } } // Copy the same buffer to each expert's slice void* expert_outputs_data; NCCLCHECK(ncclEpTensorGetData(expert_outputs, &expert_outputs_data)); for (int e = 0; e < num_local_experts; ++e) { size_t offset_bytes = static_cast(e) * config.max_tokens_per_rank * hidden * nRanks * 2; CUDACHECK(cudaMemcpy(static_cast(expert_outputs_data) + offset_bytes, expert_outputs_host, config.max_tokens_per_rank * hidden * 2, cudaMemcpyHostToDevice)); } delete[] expert_outputs_host; } else { // HT mode: expert_outputs is 2D [num_recv_tokens, hidden] NCCLCHECK(ncclEpTensorCreate(ep_group, &expert_outputs, 2, ncclBfloat16, NCCL_EP_TENSOR_TAG_TOKENS, nullptr, num_recv_tokens, static_cast(hidden))); // Fill expert outputs with test values // The ith element will be (i+1)*2 uint16_t *expert_outputs_host = new uint16_t[num_recv_tokens * hidden](); for (unsigned int t = 0; t < num_recv_tokens; ++t) { for (int j = 0; j < ELEMENTS_TESTED_PER_TOKEN; ++j) { expert_outputs_host[t * hidden + j] = float_to_bf16(static_cast((j + 1) * 2)); } } void* expert_outputs_data; NCCLCHECK(ncclEpTensorGetData(expert_outputs, &expert_outputs_data)); CUDACHECK(cudaMemcpy(expert_outputs_data, expert_outputs_host, num_recv_tokens * hidden * 2, cudaMemcpyHostToDevice)); delete[] expert_outputs_host; } // Create combined output tensor ncclNDTensor_t combined_output; NCCLCHECK(ncclEpTensorCreate(ep_group, &combined_output, 2, ncclBfloat16, NCCL_EP_TENSOR_TAG_TOKENS, nullptr, static_cast(num_tokens), static_cast(hidden))); // Setup combine inputs and outputs ncclNDTensor_t combine_inputs[1]; combine_inputs[0] = expert_outputs; ncclNDTensor_t combine_outputs[1]; combine_outputs[0] = combined_output; ncclNDTensor_t combine_local_tensors[1]; if (algorithm == NCCL_EP_ALGO_LOW_LATENCY) { combine_local_tensors[0] = topk_weights; } printf("Rank %d: Testing ncclEpCombine (send_only=%s)\n", myRank, combine_send_only ? "true" : "false"); NCCLCHECK(ncclEpCombine(ep_handle, combine_inputs, 1, combine_outputs, 1, combine_local_tensors, algorithm == NCCL_EP_ALGO_LOW_LATENCY ? 1 : 0, combine_send_only /* send_only */, nullptr /* config reserved */, s)); NCCLCHECK(ncclEpComplete(ep_handle, nullptr, s)); CUDACHECK(cudaStreamSynchronize(s)); // Verify combine output - check ELEMENTS_TESTED_PER_TOKEN elements per token int combine_errors = 0; if (!random_mode) { uint16_t *combined_output_host = new uint16_t[num_tokens * hidden](); void* combined_output_data; NCCLCHECK(ncclEpTensorGetData(combined_output, &combined_output_data)); CUDACHECK(cudaMemcpy(combined_output_host, combined_output_data, num_tokens * hidden * 2, cudaMemcpyDeviceToHost)); // Since all experts output the same values and we use equal weights, // each token should have the same values as the expert outputs: (i+1)*2 for (int i = 0; i < num_tokens; ++i) { for (int j = 0; j < ELEMENTS_TESTED_PER_TOKEN; ++j) { uint16_t expected = float_to_bf16(static_cast((j + 1) * 2)); uint16_t actual = (combined_output_host[i * hidden + j]); if (actual != expected) { printf("Combine check failed! Rank %d, token %d, element %d: expected %d, got %d\n", myRank, i, j, expected, actual); combine_errors++; if (combine_errors >= 5) break; // Limit error output } } if (combine_errors >= 5) break; } delete[] combined_output_host; } if (random_mode) { printf("Rank %d: Combine flow completed (random mode, checks skipped)\n", myRank); } else if (combine_errors == 0) { printf("Rank %d: Combine verification PASSED! All %d tokens with %d elements each correctly combined\n", myRank, num_tokens, hidden); } else { printf("Rank %d: Combine verification FAILED with %d errors\n", myRank, combine_errors); printf("Rank %d: Exiting test due to combine failure\n", myRank); exit(EXIT_FAILURE); } // Cached mode test: repeat dispatch and combine calls with the same inputs but new outputs // Compare results between first and second phase to verify cached mode correctness if (cached_mode) { // Cached mode is only supported in HT modes (not LL) if (algorithm == NCCL_EP_ALGO_LOW_LATENCY) { printf("Rank %d: Error - cached mode is only supported in HT modes (not LL)\n", myRank); exit(EXIT_FAILURE); } printf("Rank %d: Testing cached mode (%s)\n", myRank, algorithm_name); // Save first phase dispatch outputs to host for comparison (only if not in random mode) uint16_t *first_dispatch_output0 = nullptr; float *first_dispatch_output1 = nullptr; int64_t *first_dispatch_output2 = nullptr; uint16_t *first_combine_output = nullptr; if (!random_mode) { first_dispatch_output0 = new uint16_t[num_recv_tokens * hidden]; first_dispatch_output1 = new float[num_recv_tokens * top_k]; first_dispatch_output2 = new int64_t[num_recv_tokens * top_k]; void* out0_data; NCCLCHECK(ncclEpTensorGetData(outputs[0], &out0_data)); CUDACHECK(cudaMemcpy(first_dispatch_output0, out0_data, num_recv_tokens * hidden * sizeof(uint16_t), cudaMemcpyDeviceToHost)); void* out1_data; NCCLCHECK(ncclEpTensorGetData(outputs[1], &out1_data)); CUDACHECK(cudaMemcpy(first_dispatch_output1, out1_data, num_recv_tokens * top_k * sizeof(float), cudaMemcpyDeviceToHost)); void* out2_data; NCCLCHECK(ncclEpTensorGetData(outputs[2], &out2_data)); CUDACHECK(cudaMemcpy(first_dispatch_output2, out2_data, num_recv_tokens * top_k * sizeof(int64_t), cudaMemcpyDeviceToHost)); // Save first phase combine output to host for comparison first_combine_output = new uint16_t[num_tokens * hidden]; void* comb_out_data; NCCLCHECK(ncclEpTensorGetData(combined_output, &comb_out_data)); CUDACHECK(cudaMemcpy(first_combine_output, comb_out_data, num_tokens * hidden * sizeof(uint16_t), cudaMemcpyDeviceToHost)); } // Allocate new output tensors for second phase dispatch ncclNDTensor_t cached_outputs[1]; NCCLCHECK(ncclEpTensorCreate(ep_group, &cached_outputs[0], 2, ncclBfloat16, NCCL_EP_TENSOR_TAG_TOKENS, nullptr, static_cast(num_recv_tokens), static_cast(hidden))); // Allocate new output tensors for second phase combine ncclNDTensor_t cached_combined_output; NCCLCHECK(ncclEpTensorCreate(ep_group, &cached_combined_output, 2, ncclBfloat16, NCCL_EP_TENSOR_TAG_TOKENS, nullptr, static_cast(num_tokens), static_cast(hidden))); ncclNDTensor_t cached_combined_topk_weights; NCCLCHECK(ncclEpTensorCreate(ep_group, &cached_combined_topk_weights, 2, ncclFloat32, NCCL_EP_TENSOR_TAG_TOPK_WEIGHTS, nullptr, static_cast(num_tokens), static_cast(top_k))); ncclNDTensor_t cached_combine_outputs[2]; cached_combine_outputs[0] = cached_combined_output; cached_combine_outputs[1] = cached_combined_topk_weights; // Setup combine inputs with per-received-token topk_weights from dispatch output. // HT backward combine expects COMBINE_INPUT_TOPK_WEIGHTS to align with COMBINE_INPUT_TOKENS. // Create a new tensor with the correct COMBINE_INPUT tag and copy data from dispatch output. ncclNDTensor_t cached_combine_topk_weights_input; NCCLCHECK(ncclEpTensorCreate(ep_group, &cached_combine_topk_weights_input, 2, ncclFloat32, NCCL_EP_TENSOR_TAG_TOPK_WEIGHTS, nullptr, static_cast(num_recv_tokens), static_cast(top_k))); void* cached_ctwi_data; NCCLCHECK(ncclEpTensorGetData(cached_combine_topk_weights_input, &cached_ctwi_data)); void* out1_data_for_copy; NCCLCHECK(ncclEpTensorGetData(outputs[1], &out1_data_for_copy)); CUDACHECK(cudaMemcpy(cached_ctwi_data, out1_data_for_copy, num_recv_tokens * top_k * sizeof(float), cudaMemcpyDeviceToDevice)); ncclNDTensor_t cached_combine_inputs[2]; cached_combine_inputs[0] = expert_outputs; cached_combine_inputs[1] = cached_combine_topk_weights_input; printf("Rank %d: Testing cached mode - second ncclEpDispatch call (send_only=%s)\n", myRank, dispatch_send_only ? "true" : "false"); NCCLCHECK(ncclEpDispatch(ep_handle, inputs, 1, cached_outputs, 1, nullptr, 0, dispatch_send_only /* send_only */, &dispatch_config, s)); printf("Rank %d: Testing cached mode - second ncclEpComplete (dispatch)\n", myRank); NCCLCHECK(ncclEpComplete(ep_handle, nullptr, s)); CUDACHECK(cudaStreamSynchronize(s)); printf("Rank %d: Testing cached mode - second ncclEpCombine call (send_only=%s)\n", myRank, combine_send_only ? "true" : "false"); NCCLCHECK(ncclEpCombine(ep_handle, cached_combine_inputs, 2, cached_combine_outputs, 2, nullptr, 0, /* no local_tensors for HT combine */ combine_send_only /* send_only */, nullptr /* config reserved */, s)); printf("Rank %d: Testing cached mode - second ncclEpComplete (combine)\n", myRank); NCCLCHECK(ncclEpComplete(ep_handle, nullptr, s)); CUDACHECK(cudaStreamSynchronize(s)); // Copy second phase outputs to host for comparison (only if not in random mode) int cached_dispatch_errors = 0; int cached_combine_errors = 0; if (!random_mode) { uint16_t *second_dispatch_output0 = new uint16_t[num_recv_tokens * hidden]; void* cached_out0_data; NCCLCHECK(ncclEpTensorGetData(cached_outputs[0], &cached_out0_data)); CUDACHECK(cudaMemcpy(second_dispatch_output0, cached_out0_data, num_recv_tokens * hidden * sizeof(uint16_t), cudaMemcpyDeviceToHost)); uint16_t *second_combine_output = new uint16_t[num_tokens * hidden]; void* cached_comb_out_data; NCCLCHECK(ncclEpTensorGetData(cached_combined_output, &cached_comb_out_data)); CUDACHECK(cudaMemcpy(second_combine_output, cached_comb_out_data, num_tokens * hidden * sizeof(uint16_t), cudaMemcpyDeviceToHost)); float *second_combine_topk_weights = new float[num_tokens * top_k]; void* cached_comb_tw_data; NCCLCHECK(ncclEpTensorGetData(cached_combined_topk_weights, &cached_comb_tw_data)); CUDACHECK(cudaMemcpy(second_combine_topk_weights, cached_comb_tw_data, num_tokens * top_k * sizeof(float), cudaMemcpyDeviceToHost)); // Compare dispatch outputs between first and second phase for (unsigned int i = 0; i < num_recv_tokens * hidden; ++i) { if (first_dispatch_output0[i] != second_dispatch_output0[i]) { if (cached_dispatch_errors < 5) { printf("Rank %d: Cached dispatch output0 mismatch at %u: first=%u, second=%u\n", myRank, i, first_dispatch_output0[i], second_dispatch_output0[i]); } cached_dispatch_errors++; } } // Compare combine outputs between first and second phase for (unsigned int i = 0; i < num_tokens * hidden; ++i) { if (first_combine_output[i] != second_combine_output[i]) { if (cached_combine_errors < 5) { printf("Rank %d: Cached combine output mismatch at %u: first=%u, second=%u\n", myRank, i, first_combine_output[i], second_combine_output[i]); } cached_combine_errors++; } } // Verify combined topk_weights output (should match input topk_weights: 1.0f / top_k) float expected_weight = 1.0f / top_k; for (int i = 0; i < num_tokens * top_k; ++i) { if (second_combine_topk_weights[i] != expected_weight) { if (cached_combine_errors < 5) { printf("Rank %d: Cached combine topk_weights mismatch at %d: expected=%f, got=%f\n", myRank, i, expected_weight, second_combine_topk_weights[i]); } cached_combine_errors++; } } delete[] second_dispatch_output0; delete[] second_combine_output; delete[] second_combine_topk_weights; } if (random_mode) { printf("Rank %d: Cached mode completed (random mode, checks skipped)\n", myRank); } else if (cached_dispatch_errors == 0 && cached_combine_errors == 0) { printf("Rank %d: Cached mode verification PASSED - dispatch and combine outputs match\n", myRank); } else { printf("Rank %d: Cached mode verification FAILED - dispatch errors: %d, combine errors: %d\n", myRank, cached_dispatch_errors, cached_combine_errors); exit(EXIT_FAILURE); } // Clean up cached mode tensors if (!random_mode) { delete[] first_dispatch_output0; delete[] first_dispatch_output1; delete[] first_dispatch_output2; delete[] first_combine_output; } ncclEpTensorDestroy(ep_group, cached_outputs[0]); ncclEpTensorDestroy(ep_group, cached_combined_output); ncclEpTensorDestroy(ep_group, cached_combined_topk_weights); ncclEpTensorDestroy(ep_group, cached_combine_topk_weights_input); printf("Rank %d: Cached mode - second dispatch and combine calls completed successfully\n", myRank); } // Clean up first phase host buffer (kept for cached mode comparison in HT mode) if (first_dispatch_output0_host) delete[] first_dispatch_output0_host; // Clean up combine tensors ncclEpTensorDestroy(ep_group, expert_outputs); ncclEpTensorDestroy(ep_group, topk_weights); ncclEpTensorDestroy(ep_group, combined_output); NCCLCHECK(ncclEpHandleDestroy(ep_handle)); NCCLCHECK(ncclEpGroupDestroy(ep_group, s)); // finalizing NCCL ncclCommDestroy(comm); delete[] topk_idx_host; ncclEpTensorDestroy(ep_group, topk_idx); // Free recv_expert_counter host tensor (uses cudaFreeHost, not cudaFree) only if it was allocated if (disable_max_tokens && handle_local_tensors[0] != nullptr) { void* recv_counter_data; ncclEpTensorGetData(handle_recv_expert_counter, &recv_counter_data); cudaFreeHost(recv_counter_data); ncclEpTensorDestroy(ep_group, handle_recv_expert_counter); } ncclEpTensorDestroy(ep_group, inputs[0]); ncclEpTensorDestroy(ep_group, outputs[0]); if (algorithm != NCCL_EP_ALGO_LOW_LATENCY) { ncclEpTensorDestroy(ep_group, outputs[1]); ncclEpTensorDestroy(ep_group, outputs[2]); } if (algorithm == NCCL_EP_ALGO_LOW_LATENCY) { ncclEpTensorDestroy(ep_group, local_tensors[0]); } // finalizing MPI MPICHECK(MPI_Finalize()); // Needed for cuda-memcheck --leak-check full cudaDeviceReset(); printf("[MPI Rank %d] Success \n", myRank); return 0; }