2019-04-10 15:28:40 -07:00
2017-08-08 16:18:34 -07:00
/*************************************************************************
2022-09-20 02:21:36 -07:00
* Copyright (c) 2016-2022, NVIDIA CORPORATION. All rights reserved.
2022-10-14 16:02:54 -05:00
* Modifications Copyright (c) 2019-2022 Advanced Micro Devices, Inc. All rights reserved.
2024-03-08 08:54:41 +08:00
* Modifications Copyright (c) Microsoft Corporation. Licensed under the MIT License.
2017-08-08 16:18:34 -07:00
*
2019-03-06 18:17:20 -08:00
* See LICENSE.txt for license information
2017-08-08 16:18:34 -07:00
************************************************************************/
2024-03-05 09:47:18 -07:00
#include "cuda_runtime.h"
2024-03-09 02:02:40 +08:00
#include "rccl_float8.h"
2024-04-23 17:00:20 -05:00
#include <hip/hip_bfloat16.h>
2017-08-08 16:18:34 -07:00
#include "common.h"
#include <pthread.h>
#include <cstdio>
2022-08-19 15:15:10 -05:00
#include <type_traits>
2017-08-08 16:18:34 -07:00
#include <getopt.h>
2019-03-06 18:17:20 -08:00
#include <libgen.h>
2025-01-23 11:09:09 -08:00
#include <string.h>
#include <ctype.h>
2024-03-05 09:47:18 -07:00
#include "cuda.h"
2025-01-13 15:28:29 -08:00
#include <vector>
#include <utility>
2025-06-03 11:43:02 -04:00
#include <errno.h> /* program_invocation_short_name */
2017-08-08 16:18:34 -07:00
2020-03-19 10:18:39 -07:00
//#define DEBUG_PRINT
2024-03-05 09:47:18 -07:00
#include "verifiable.h"
2024-03-28 14:03:59 -05:00
#include "git_version.h"
2022-08-19 15:15:10 -05:00
2025-05-21 09:40:26 -07:00
#define DIVUP(x, y) \
(((x)+(y)-1)/(y))
2021-06-17 14:08:43 -07:00
int test_ncclVersion = 0; // init'd with ncclGetVersion()
2024-05-10 08:46:13 -07:00
int32_t gpu_block3;
2024-06-04 11:35:39 -05:00
size_t cache_bytes = 192 * 1024 * 1024; // Use 192MB
2021-06-17 14:08:43 -07:00
2025-05-14 15:30:07 -05:00
// RCCL_FLOAT8 support
bool rccl_float8_useFnuz = false;
bool IsArchMatch(char const* arch, char const* target) {
// helper function to reduce clutter in code elsewhere. Returns true on match.
return (strncmp(arch, target, strlen(target)) == 0);
}
2017-08-08 16:18:34 -07:00
#if NCCL_MAJOR >= 2
2021-09-13 14:43:22 -07:00
ncclDataType_t test_types[ncclNumTypes] = {
ncclInt8, ncclUint8, ncclInt32, ncclUint32, ncclInt64, ncclUint64, ncclHalf, ncclFloat, ncclDouble
2025-04-18 19:20:59 -07:00
#if HAVE_BF16
2021-09-13 14:43:22 -07:00
, ncclBfloat16
#endif
2025-04-18 19:20:59 -07:00
#if HAVE_FP8
2025-05-14 15:30:07 -05:00
, ncclFloat8e4m3, ncclFloat8e5m2
2024-03-08 08:54:41 +08:00
#endif
2021-09-13 14:43:22 -07:00
};
const char *test_typenames[ncclNumTypes] = {
"int8", "uint8", "int32", "uint32", "int64", "uint64", "half", "float", "double"
2025-04-18 19:20:59 -07:00
#if HAVE_BF16
2021-09-13 14:43:22 -07:00
, "bfloat16"
#endif
2025-04-18 19:20:59 -07:00
#if HAVE_FP8
2024-03-08 08:54:41 +08:00
, "fp8_e4m3", "fp8_e5m2"
#endif
2021-09-13 14:43:22 -07:00
};
int test_typenum = -1;
2021-06-17 14:08:43 -07:00
2021-09-13 14:43:22 -07:00
const char *test_opnames[] = {"sum", "prod", "max", "min", "avg", "mulsum"};
ncclRedOp_t test_ops[] = {ncclSum, ncclProd, ncclMax, ncclMin
#if NCCL_VERSION_CODE >= NCCL_VERSION(2,10,0)
, ncclAvg
#endif
#if NCCL_VERSION_CODE >= NCCL_VERSION(2,11,0)
, ncclNumOps // stand in for ncclRedOpCreatePreMulSum() created on-demand
#endif
};
int test_opnum = -1;
2021-06-17 14:08:43 -07:00
#else
2021-09-13 14:43:22 -07:00
ncclDataType_t test_types[ncclNumTypes] = {ncclChar, ncclInt, ncclHalf, ncclFloat, ncclDouble, ncclInt64, ncclUint64};
const char *test_typenames[ncclNumTypes] = {"char", "int", "half", "float", "double", "int64", "uint64"};
int test_typenum = 7;
const char *test_opnames[] = {"sum", "prod", "max", "min"};
ncclRedOp_t test_ops[] = {ncclSum, ncclProd, ncclMax, ncclMin};
int test_opnum = 4;
2021-06-17 14:08:43 -07:00
#endif
2017-08-08 16:18:34 -07:00
2020-12-15 22:05:50 -05:00
const char *test_memorytypes[nccl_NUM_MTYPES] = {"coarse", "fine", "host", "managed"};
2017-08-08 16:18:34 -07:00
2022-09-20 02:21:36 -07:00
// For libnccl's < 2.13
extern "C" __attribute__((weak)) char const* ncclGetLastError(ncclComm_t comm) {
return "";
}
int is_main_proc = 0;
2017-08-08 16:18:34 -07:00
thread_local int is_main_thread = 0;
2019-03-06 18:17:20 -08:00
// Command line parameter defaults
static int nThreads = 1;
static int nGpus = 1;
static size_t minBytes = 32*1024*1024;
static size_t maxBytes = 32*1024*1024;
static size_t stepBytes = 1*1024*1024;
static size_t stepFactor = 1;
2017-08-08 16:18:34 -07:00
static int datacheck = 1;
static int warmup_iters = 5;
static int iters = 20;
2019-03-06 18:17:20 -08:00
static int agg_iters = 1;
2024-07-25 21:47:40 -07:00
static int run_cycles = 1;
2017-08-08 16:18:34 -07:00
static int ncclop = ncclSum;
static int nccltype = ncclFloat;
static int ncclroot = 0;
static int parallel_init = 0;
static int blocking_coll = 0;
2019-05-01 12:58:04 -07:00
static int memorytype = 0;
2020-08-21 21:34:55 +00:00
static uint32_t cumask[4];
2022-09-20 02:21:36 -07:00
static int streamnull = 0;
static int timeout = 0;
2021-06-28 14:19:45 -07:00
static int cudaGraphLaunches = 0;
2025-01-13 15:28:29 -08:00
std::string output_file;
std::string output_format;
2022-09-20 02:21:36 -07:00
static int report_cputime = 0;
2021-06-30 19:36:07 -07:00
// Report average iteration time: (0=RANK0,1=AVG,2=MIN,3=MAX)
static int average = 1;
2022-03-18 11:42:15 -04:00
static int numDevices = 1;
2022-10-12 17:28:04 -07:00
static int delay_inout_place = 0;
2024-01-04 16:20:42 -06:00
static int enable_out_of_place = 1;
2025-04-03 17:31:54 -05:00
static int enable_in_place = 1;
2024-05-07 11:09:32 -05:00
static int enable_cache_flush = 0;
2024-06-04 11:35:39 -05:00
static int enable_rotating_tensor = 0;
2024-02-28 05:18:40 -08:00
#if NCCL_VERSION_CODE >= NCCL_VERSION(2,19,0)
2025-05-19 18:20:22 -07:00
#define LOCAL_REGISTER 1
#define SYMMETRIC_REGISTER 2
2024-02-28 05:18:40 -08:00
static int local_register = 0;
#endif
2025-04-18 19:20:59 -07:00
static int minCudaArch = 1<<30;
2017-08-08 16:18:34 -07:00
2025-01-13 15:28:29 -08:00
Reporter::Reporter(std::string fileName, std::string outputFormat) : _outputFormat(outputFormat) {
if (!fileName.empty()) {
if (isMainThread()) {
_out = std::ofstream(fileName, std::ios_base::out);
_outputValid = true;
}
}
}
2025-04-11 12:00:15 -05:00
void Reporter::setParameters(const size_t numCycle, const char* name, const char* typeName, const char* opName) {
2025-01-13 15:28:29 -08:00
if (!isMainThread() || !_outputValid)
return;
2025-04-11 12:00:15 -05:00
_numCycle = numCycle;
2025-01-13 15:28:29 -08:00
_collectiveName = name;
_typeName = typeName;
_opName = opName;
}
void Reporter::addResult(int gpusPerRank, int ranksPerNode, int totalRanks, size_t numBytes, int inPlace, double timeUsec, double algBw, double busBw, int64_t wrongElts) {
if (!isMainThread() || !_outputValid)
return;
std::vector<std::pair<std::string, std::string>> outputValuesKeys;
std::string wrongEltsStr = (wrongElts == -1) ? "N/A" : std::to_string(wrongElts);
int nodes = totalRanks / ranksPerNode;
2025-04-11 12:00:15 -05:00
outputValuesKeys.push_back(makeValueKeyPair(_numCycle, "numCycle"));
2025-01-13 15:28:29 -08:00
outputValuesKeys.push_back(makeValueKeyPair(_collectiveName, "name"));
#ifdef MPI_SUPPORT
outputValuesKeys.push_back(makeValueKeyPair(nodes, "nodes"));
outputValuesKeys.push_back(makeValueKeyPair(totalRanks, "ranks"));
outputValuesKeys.push_back(makeValueKeyPair(ranksPerNode, "ranksPerNode"));
outputValuesKeys.push_back(makeValueKeyPair(gpusPerRank, "gpusPerRank"));
#else
outputValuesKeys.push_back(makeValueKeyPair(gpusPerRank, "gpus"));
#endif
outputValuesKeys.push_back(makeValueKeyPair(numBytes, "size"));
outputValuesKeys.push_back(makeValueKeyPair(_typeName, "type"));
outputValuesKeys.push_back(makeValueKeyPair(_opName, "redop"));
outputValuesKeys.push_back(makeValueKeyPair(inPlace, "inPlace"));
outputValuesKeys.push_back(makeValueKeyPair(timeUsec, "time"));
outputValuesKeys.push_back(makeValueKeyPair(algBw, "algBw"));
outputValuesKeys.push_back(makeValueKeyPair(busBw, "busBw"));
outputValuesKeys.push_back(makeValueKeyPair(wrongEltsStr, "wrong"));
2025-07-30 17:28:04 -05:00
_outputData.push_back(outputValuesKeys);
}
void Reporter::writeFile() {
if (!isMainThread() || !_outputValid)
return;
if (_outputFormat == "csv") {
_out << "numCycle,";
_out << "collective,";
#ifdef MPI_SUPPORT
_out << "ranks,rankspernode,gpusperrank,";
#else
_out << "gpus,";
#endif
_out << "size,type,redop,inplace,time,algbw,busbw,#wrong\n";
for (auto iterEntries = _outputData.begin(); iterEntries != _outputData.end(); ++iterEntries) {
for (auto iterVals = (*iterEntries).begin(); iterVals != (*iterEntries).end(); ++iterVals) {
_out << iterVals->first;
if (std::next(iterVals) != (*iterEntries).end()) {
_out << ",";
}
2025-01-13 15:28:29 -08:00
}
2025-07-30 17:28:04 -05:00
_out << std::endl;
}
} else { //json
_out << "[" << std::endl;
for (auto iterEntries = _outputData.begin(); iterEntries != _outputData.end(); ++iterEntries) {
for (auto iterVals = (*iterEntries).begin(); iterVals != (*iterEntries).end(); ++iterVals) {
if (iterVals == (*iterEntries).begin()) {
_out << "{";
}
_out << "\"" << iterVals->second << "\":" << iterVals->first;
if (std::next(iterVals) != (*iterEntries).end()) {
_out << ", ";
}
2025-01-13 15:28:29 -08:00
}
2025-07-30 17:28:04 -05:00
if (std::next(iterEntries) != _outputData.end()) {
_out << "}," << std::endl;
2025-01-13 15:28:29 -08:00
} else {
2025-07-30 17:28:04 -05:00
_out << "}" << std::endl;
2025-01-13 15:28:29 -08:00
}
}
2025-07-30 17:28:04 -05:00
_out << "]" << std::endl;
2025-01-13 15:28:29 -08:00
}
}
2025-07-30 17:28:04 -05:00
2025-01-13 15:28:29 -08:00
bool Reporter::isMainThread() { return is_main_thread == 1; }
2021-06-28 18:23:12 -07:00
#define NUM_BLOCKS 32
2017-08-08 16:18:34 -07:00
2024-05-07 11:09:32 -05:00
#ifndef CHECK_HIP_ERROR
#define CHECK_HIP_ERROR(error) \
if(error != hipSuccess) \
{ \
fprintf(stderr, \
"Hip error: '%s'(%d) at %s:%d\n", \
hipGetErrorString(error), \
error, \
__FILE__, \
__LINE__); \
exit(EXIT_FAILURE); \
}
#endif
extern "C" __global__ void flush_icache()
{
asm __volatile__("s_icache_inv \n\t"
"s_nop 0 \n\t"
"s_nop 0 \n\t"
"s_nop 0 \n\t"
"s_nop 0 \n\t"
"s_nop 0 \n\t"
"s_nop 0 \n\t"
"s_nop 0 \n\t"
"s_nop 0 \n\t"
"s_nop 0 \n\t"
"s_nop 0 \n\t"
"s_nop 0 \n\t"
"s_nop 0 \n\t"
"s_nop 0 \n\t"
"s_nop 0 \n\t"
"s_nop 0 \n\t"
"s_nop 0 \n\t" ::
:);
}
2021-06-02 17:52:11 -07:00
static double parsesize(const char *value) {
2017-08-08 16:18:34 -07:00
long long int units;
double size;
2024-03-08 08:54:41 +08:00
char size_lit[2];
2017-08-08 16:18:34 -07:00
2024-03-08 08:54:41 +08:00
int count = sscanf(value, "%lf %1s", &size, size_lit);
2017-08-08 16:18:34 -07:00
2021-06-02 17:52:11 -07:00
switch (count) {
case 2:
2024-03-08 08:54:41 +08:00
switch (size_lit[0]) {
2021-06-02 17:52:11 -07:00
case 'G':
case 'g':
units = 1024*1024*1024;
break;
case 'M':
case 'm':
units = 1024*1024;
break;
case 'K':
case 'k':
units = 1024;
break;
default:
return -1.0;
};
break;
case 1:
units = 1;
break;
default:
return -1.0;
2017-08-08 16:18:34 -07:00
}
2021-06-02 17:52:11 -07:00
return size * units;
2017-08-08 16:18:34 -07:00
}
2022-03-18 11:42:15 -04:00
static bool minReqVersion(int rmajor, int rminor, int rpatch)
{
int version;
int major, minor, patch, rem;
ncclGetVersion(&version);
if (version < 10000) {
major = version/1000;
rem = version%1000;
minor = rem/100;
patch = rem%100;
}
else {
major = version/10000;
rem = version%10000;
minor = rem/100;
patch = rem%100;
}
if (major < rmajor) return false;
else if (major > rmajor) return true;
// major == rmajor
if (minor < rminor) return false;
else if (minor > rminor) return true;
// major == rmajor && minor == rminor
if (patch < rpatch) return false;
return true;
}
2022-08-19 15:15:10 -05:00
testResult_t CheckDelta(void* results, void* expected, size_t count, size_t offset, ncclDataType_t type, ncclRedOp_t op, uint64_t seed, int nranks, int64_t *wrongEltN) {
2025-05-14 15:30:07 -05:00
CUDACHECK(ncclVerifiableVerify(results, expected, count, (int)type, (int)op, nranks, seed, offset, wrongEltN, cudaStreamDefault));
2024-03-05 09:47:18 -07:00
CUDACHECK(cudaDeviceSynchronize());
2019-03-06 18:17:20 -08:00
return testSuccess;
2017-08-08 16:18:34 -07:00
}
2022-08-19 15:15:10 -05:00
testResult_t InitDataReduce(void* data, const size_t count, const size_t offset, ncclDataType_t type, ncclRedOp_t op, uint64_t seed, int nranks) {
2025-05-14 15:30:07 -05:00
CUDACHECK(ncclVerifiablePrepareExpected(data, count, (int)type, (int)op, nranks, seed, offset, cudaStreamDefault));
2022-08-19 15:15:10 -05:00
return testSuccess;
2017-08-08 16:18:34 -07:00
}
2022-08-19 15:15:10 -05:00
testResult_t InitData(void* data, const size_t count, size_t offset, ncclDataType_t type, ncclRedOp_t op, uint64_t seed, int nranks, int rank) {
2025-05-14 15:30:07 -05:00
CUDACHECK(ncclVerifiablePrepareInput(data, count, (int)type, (int)op, nranks, rank, seed, offset, cudaStreamDefault));
2022-08-19 15:15:10 -05:00
return testSuccess;
2021-03-15 14:44:06 -07:00
}
2017-08-08 16:18:34 -07:00
2022-08-19 15:15:10 -05:00
void Barrier(struct threadArgs *args) {
thread_local int epoch = 0;
static pthread_mutex_t lock[2] = {PTHREAD_MUTEX_INITIALIZER, PTHREAD_MUTEX_INITIALIZER};
static pthread_cond_t cond[2] = {PTHREAD_COND_INITIALIZER, PTHREAD_COND_INITIALIZER};
static int counter[2] = {0, 0};
2021-06-17 14:08:43 -07:00
2022-08-19 15:15:10 -05:00
pthread_mutex_lock(&lock[epoch]);
if(++counter[epoch] == args->nThreads)
pthread_cond_broadcast(&cond[epoch]);
2021-09-13 14:43:22 -07:00
2022-08-19 15:15:10 -05:00
if(args->thread+1 == args->nThreads) {
while(counter[epoch] != args->nThreads)
pthread_cond_wait(&cond[epoch], &lock[epoch]);
#ifdef MPI_SUPPORT
MPI_Barrier(MPI_COMM_WORLD);
#endif
counter[epoch] = 0;
pthread_cond_broadcast(&cond[epoch]);
2017-08-08 16:18:34 -07:00
}
2022-08-19 15:15:10 -05:00
else {
while(counter[epoch] != 0)
pthread_cond_wait(&cond[epoch], &lock[epoch]);
2017-08-08 16:18:34 -07:00
}
2022-08-19 15:15:10 -05:00
pthread_mutex_unlock(&lock[epoch]);
epoch ^= 1;
2017-08-08 16:18:34 -07:00
}
2022-08-19 15:15:10 -05:00
// Inter-thread/process barrier+allreduce. The quality of the return value
// for average=0 (which means broadcast from rank=0) is dubious. The returned
// value will actually be the result of process-local broadcast from the local thread=0.
2017-08-08 16:18:34 -07:00
template<typename T>
2022-08-19 15:15:10 -05:00
void Allreduce(struct threadArgs* args, T* value, int average) {
thread_local int epoch = 0;
static pthread_mutex_t lock[2] = {PTHREAD_MUTEX_INITIALIZER, PTHREAD_MUTEX_INITIALIZER};
static pthread_cond_t cond[2] = {PTHREAD_COND_INITIALIZER, PTHREAD_COND_INITIALIZER};
static T accumulator[2];
static int counter[2] = {0, 0};
pthread_mutex_lock(&lock[epoch]);
if(counter[epoch] == 0) {
if(average != 0 || args->thread == 0) accumulator[epoch] = *value;
2017-08-08 16:18:34 -07:00
} else {
2022-08-19 15:15:10 -05:00
switch(average) {
case /*r0*/ 0: if(args->thread == 0) accumulator[epoch] = *value; break;
case /*avg*/1: accumulator[epoch] += *value; break;
case /*min*/2: accumulator[epoch] = std::min<T>(accumulator[epoch], *value); break;
case /*max*/3: accumulator[epoch] = std::max<T>(accumulator[epoch], *value); break;
case /*sum*/4: accumulator[epoch] += *value; break;
2017-08-08 16:18:34 -07:00
}
}
2022-08-19 15:15:10 -05:00
if(++counter[epoch] == args->nThreads)
pthread_cond_broadcast(&cond[epoch]);
if(args->thread+1 == args->nThreads) {
while(counter[epoch] != args->nThreads)
pthread_cond_wait(&cond[epoch], &lock[epoch]);
#ifdef MPI_SUPPORT
if(average != 0) {
static_assert(std::is_same<T, long long>::value || std::is_same<T, double>::value, "Allreduce<T> only for T in {long long, double}");
MPI_Datatype ty = std::is_same<T, long long>::value ? MPI_LONG_LONG :
std::is_same<T, double>::value ? MPI_DOUBLE :
MPI_Datatype();
MPI_Op op = average == 1 ? MPI_SUM :
average == 2 ? MPI_MIN :
average == 3 ? MPI_MAX :
average == 4 ? MPI_SUM : MPI_Op();
MPI_Allreduce(MPI_IN_PLACE, (void*)&accumulator[epoch], 1, ty, op, MPI_COMM_WORLD);
2021-07-08 16:42:40 -07:00
}
2022-08-19 15:15:10 -05:00
#endif
2017-08-08 16:18:34 -07:00
2022-09-20 02:21:36 -07:00
if(average == 1) accumulator[epoch] /= args->totalProcs*args->nThreads;
2022-08-19 15:15:10 -05:00
counter[epoch] = 0;
pthread_cond_broadcast(&cond[epoch]);
2021-07-08 16:42:40 -07:00
}
2022-08-19 15:15:10 -05:00
else {
while(counter[epoch] != 0)
pthread_cond_wait(&cond[epoch], &lock[epoch]);
2021-07-08 16:42:40 -07:00
}
2022-08-19 15:15:10 -05:00
pthread_mutex_unlock(&lock[epoch]);
*value = accumulator[epoch];
epoch ^= 1;
2017-08-08 16:18:34 -07:00
}
2022-08-19 15:15:10 -05:00
testResult_t CheckData(struct threadArgs* args, ncclDataType_t type, ncclRedOp_t op, int root, int in_place, int64_t *wrongElts) {
int nranks = args->nProcs*args->nGpus*args->nThreads;
2017-08-08 16:18:34 -07:00
size_t count = args->expectedBytes/wordSize(type);
2022-08-19 15:15:10 -05:00
int64_t *wrongPerGpu = nullptr;
2024-03-05 09:47:18 -07:00
CUDACHECK(hipHostMalloc((void**)&wrongPerGpu, args->nGpus*sizeof(int64_t), cudaHostAllocMapped));
2025-04-03 17:31:54 -05:00
2017-08-08 16:18:34 -07:00
for (int i=0; i<args->nGpus; i++) {
int rank = ((args->proc*args->nThreads + args->thread)*args->nGpus + i);
2024-03-05 09:47:18 -07:00
CUDACHECK(cudaSetDevice(args->gpus[i]));
2017-08-08 16:18:34 -07:00
void *data = in_place ? ((void *)((uintptr_t)args->recvbuffs[i] + args->recvInplaceOffset*rank)) : args->recvbuffs[i];
2022-08-19 15:15:10 -05:00
TESTCHECK(CheckDelta(data, args->expected[i], count, 0, type, op, 0, nranks, wrongPerGpu+i));
2020-08-27 23:45:47 +00:00
2022-08-19 15:15:10 -05:00
#if 1 && DEBUG_PRINT
if (args->reportErrors && wrongPerGpu[i] != 0) {
printf("rank=%d #wrong=%d\n", rank, (int)wrongPerGpu[i]);
char *expectedHost = (char*)malloc(args->expectedBytes);
char *dataHost = (char*)malloc(args->expectedBytes);
int eltsz = wordSize(type);
2024-03-05 09:47:18 -07:00
cudaMemcpy(expectedHost, args->expected[i], args->expectedBytes, cudaMemcpyDeviceToHost);
cudaMemcpy(dataHost, data, args->expectedBytes, cudaMemcpyDeviceToHost);
2022-08-19 15:15:10 -05:00
for(int j=0; j<args->expectedBytes/eltsz; j++) {
unsigned long long want, got;
want = 0;
memcpy(&want, expectedHost + j*eltsz, eltsz);
got = 0;
memcpy(&got, dataHost + j*eltsz, eltsz);
if(want != got) {
printf(" rank=%d elt[%d]: want=0x%llx got=0x%llx\n", rank, j, want, got);
}
2020-08-27 23:45:47 +00:00
}
free(expectedHost);
free(dataHost);
2017-08-08 16:18:34 -07:00
}
#endif
}
2022-08-19 15:15:10 -05:00
*wrongElts = 0;
for (int i=0; i < args->nGpus; i++) *wrongElts += wrongPerGpu[i];
2024-03-05 09:47:18 -07:00
cudaFreeHost(wrongPerGpu);
2022-08-19 15:15:10 -05:00
if (args->reportErrors && *wrongElts) args->errors[0]++;
2019-03-06 18:17:20 -08:00
return testSuccess;
}
2025-04-03 17:31:54 -05:00
2024-03-05 09:47:18 -07:00
testResult_t testStreamSynchronize(int ngpus, cudaStream_t* streams, ncclComm_t* comms) {
cudaError_t cudaErr;
2019-03-06 18:17:20 -08:00
int remaining = ngpus;
int* done = (int*)malloc(sizeof(int)*ngpus);
memset(done, 0, sizeof(int)*ngpus);
2022-09-20 02:21:36 -07:00
timer tim;
2025-04-03 17:31:54 -05:00
2019-03-06 18:17:20 -08:00
while (remaining) {
int idle = 1;
for (int i=0; i<ngpus; i++) {
if (done[i]) continue;
2024-03-05 09:47:18 -07:00
cudaErr = cudaStreamQuery(streams[i]);
if (cudaErr == cudaSuccess) {
2019-03-06 18:17:20 -08:00
done[i] = 1;
remaining--;
idle = 0;
continue;
}
2017-08-08 16:18:34 -07:00
2024-03-05 09:47:18 -07:00
if (cudaErr != cudaErrorNotReady) CUDACHECK(cudaErr);
2019-03-06 18:17:20 -08:00
#if NCCL_VERSION_CODE >= NCCL_VERSION(2,4,0)
2021-06-17 14:08:43 -07:00
if (test_ncclVersion >= NCCL_VERSION(2,4,0) && comms) {
2019-03-06 18:17:20 -08:00
ncclResult_t ncclAsyncErr;
NCCLCHECK(ncclCommGetAsyncError(comms[i], &ncclAsyncErr));
2021-02-03 21:16:18 -05:00
if (ncclAsyncErr != ncclSuccess) {
2019-03-06 18:17:20 -08:00
// An asynchronous error happened. Stop the operation and destroy
// the communicator
for (int i=0; i<ngpus; i++)
NCCLCHECK(ncclCommAbort(comms[i]));
// Abort the perf test
NCCLCHECK(ncclAsyncErr);
2017-08-08 16:18:34 -07:00
}
2019-03-06 18:17:20 -08:00
}
2022-09-20 02:21:36 -07:00
double delta = tim.elapsed();
if (delta > timeout && timeout > 0) {
for (int i=0; i<ngpus; i++)
NCCLCHECK(ncclCommAbort(comms[i]));
char hostname[1024];
getHostName(hostname, 1024);
printf("%s: Test timeout (%ds) %s:%d\n",
hostname,
timeout,
__FILE__,__LINE__);
free(done);
return testTimeout;
}
2017-08-08 16:18:34 -07:00
#endif
2019-03-06 18:17:20 -08:00
}
2017-08-08 16:18:34 -07:00
2019-03-06 18:17:20 -08:00
// We might want to let other threads (including NCCL threads) use the CPU.
2022-08-19 15:15:10 -05:00
if (idle) sched_yield();
2017-08-08 16:18:34 -07:00
}
2019-08-09 10:22:14 -07:00
free(done);
2019-03-06 18:17:20 -08:00
return testSuccess;
2017-08-08 16:18:34 -07:00
}
2021-09-13 14:43:22 -07:00
testResult_t startColl(struct threadArgs* args, ncclDataType_t type, ncclRedOp_t opIndex, int root, int in_place, int iter) {
2017-08-08 16:18:34 -07:00
size_t count = args->nbytes / wordSize(type);
2019-03-06 18:17:20 -08:00
// Try to change offset for each iteration so that we avoid cache effects and catch race conditions in ptrExchange
2024-06-04 11:35:39 -05:00
size_t shift = 0;
if(enable_rotating_tensor) {
shift = cache_bytes * (iter % 2);
}
else {
size_t totalnbytes = std::max(args->sendBytes, args->expectedBytes);
size_t steps = totalnbytes ? args->maxbytes / totalnbytes : 1;
shift = totalnbytes * (iter % steps);
}
2025-04-03 17:31:54 -05:00
2019-03-06 18:17:20 -08:00
if (args->nGpus > 1) NCCLCHECK(ncclGroupStart());
for (int i = 0; i < args->nGpus; i++) {
2017-08-08 16:18:34 -07:00
#ifndef NCCL_MAJOR
2024-03-05 09:47:18 -07:00
CUDACHECK(cudaSetDevice(args->gpus[i]));
2017-08-08 16:18:34 -07:00
#endif
2019-03-06 18:17:20 -08:00
int rank = ((args->proc*args->nThreads + args->thread)*args->nGpus + i);
char* recvBuff = ((char*)args->recvbuffs[i]) + shift;
char* sendBuff = ((char*)args->sendbuffs[i]) + shift;
2021-09-13 14:43:22 -07:00
ncclRedOp_t op;
if(opIndex < ncclNumOps) {
op = opIndex;
}
#if NCCL_VERSION_CODE >= NCCL_VERSION(2,11,0)
else {
union {
int8_t i8; uint8_t u8; int32_t i32; uint32_t u32; int64_t i64; uint64_t u64;
half f16; float f32; double f64;
2025-04-18 19:20:59 -07:00
#if HAVE_BF16
2024-04-23 17:00:20 -05:00
hip_bfloat16 bf16;
2021-09-13 14:43:22 -07:00
#endif
2025-04-18 19:20:59 -07:00
#if HAVE_FP8
2024-03-08 08:54:41 +08:00
rccl_float8 fp8_e4m3; rccl_bfloat8 fp8_e5m2;
#endif
2021-09-13 14:43:22 -07:00
};
switch(type) {
2022-08-19 15:15:10 -05:00
case ncclInt8: i8 = ncclVerifiablePremulScalar<int8_t>(rank); break;
case ncclUint8: u8 = ncclVerifiablePremulScalar<uint8_t>(rank); break;
case ncclInt32: i32 = ncclVerifiablePremulScalar<int32_t>(rank); break;
case ncclUint32: u32 = ncclVerifiablePremulScalar<uint32_t>(rank); break;
case ncclInt64: i64 = ncclVerifiablePremulScalar<int64_t>(rank); break;
case ncclUint64: u64 = ncclVerifiablePremulScalar<uint64_t>(rank); break;
case ncclFloat16: f16 = ncclVerifiablePremulScalar<half>(rank); break;
case ncclFloat32: f32 = ncclVerifiablePremulScalar<float>(rank); break;
case ncclFloat64: f64 = ncclVerifiablePremulScalar<double>(rank); break;
2025-04-18 19:20:59 -07:00
#if HAVE_BF16
2024-04-23 17:00:20 -05:00
case ncclBfloat16: bf16 = ncclVerifiablePremulScalar<hip_bfloat16>(rank); break;
2021-09-13 14:43:22 -07:00
#endif
2025-04-18 19:20:59 -07:00
#if HAVE_FP8
2025-05-14 15:30:07 -05:00
case ncclFloat8e4m3: fp8_e4m3 = ncclVerifiablePremulScalar<rccl_float8>(rank); break;
case ncclFloat8e5m2 : fp8_e5m2 = ncclVerifiablePremulScalar<rccl_bfloat8>(rank); break;
2024-03-08 08:54:41 +08:00
#endif
2025-04-18 19:20:59 -07:00
default: break; // Just to silence clang
2021-09-13 14:43:22 -07:00
}
NCCLCHECK(ncclRedOpCreatePreMulSum(&op, &u64, type, ncclScalarHostImmediate, args->comms[i]));
}
#endif
2024-05-10 08:46:13 -07:00
if(enable_cache_flush > 0 && ((iter % enable_cache_flush) == 0)) {
2024-05-07 11:09:32 -05:00
hipLaunchKernelGGL(flush_icache, dim3(gpu_block3), dim3(64), 0, args->streams[i]);
}
2019-03-06 18:17:20 -08:00
TESTCHECK(args->collTest->runColl(
(void*)(in_place ? recvBuff + args->sendInplaceOffset*rank : sendBuff),
(void*)(in_place ? recvBuff + args->recvInplaceOffset*rank : recvBuff),
count, type, op, root, args->comms[i], args->streams[i]));
2021-09-13 14:43:22 -07:00
#if NCCL_VERSION_CODE >= NCCL_VERSION(2,11,0)
if(opIndex >= ncclNumOps) {
NCCLCHECK(ncclRedOpDestroy(op, args->comms[i]));
}
#endif
2017-08-08 16:18:34 -07:00
}
2019-03-06 18:17:20 -08:00
if (args->nGpus > 1) NCCLCHECK(ncclGroupEnd());
2017-08-08 16:18:34 -07:00
2019-03-06 18:17:20 -08:00
if (blocking_coll) {
// Complete op before returning
TESTCHECK(testStreamSynchronize(args->nGpus, args->streams, args->comms));
2017-08-08 16:18:34 -07:00
}
if (blocking_coll) Barrier(args);
2019-03-06 18:17:20 -08:00
return testSuccess;
2017-08-08 16:18:34 -07:00
}
2019-03-06 18:17:20 -08:00
testResult_t completeColl(struct threadArgs* args) {
if (blocking_coll) return testSuccess;
2017-08-08 16:18:34 -07:00
2019-03-06 18:17:20 -08:00
TESTCHECK(testStreamSynchronize(args->nGpus, args->streams, args->comms));
return testSuccess;
2017-08-08 16:18:34 -07:00
}
2019-03-06 18:17:20 -08:00
testResult_t BenchTime(struct threadArgs* args, ncclDataType_t type, ncclRedOp_t op, int root, int in_place) {
2017-08-08 16:18:34 -07:00
size_t count = args->nbytes / wordSize(type);
2021-06-28 18:23:12 -07:00
if (datacheck) {
// Initialize sendbuffs, recvbuffs and expected
TESTCHECK(args->collTest->initData(args, type, op, root, 99, in_place));
}
2019-03-06 18:17:20 -08:00
2022-10-12 17:28:04 -07:00
if (warmup_iters) {
// Sync
TESTCHECK(startColl(args, type, op, root, in_place, 0));
TESTCHECK(completeColl(args));
}
2017-08-08 16:18:34 -07:00
Barrier(args);
2022-08-09 16:45:27 -06:00
#if HIP_VERSION >= 50221310
2025-04-11 12:00:15 -05:00
std::vector<cudaGraph_t> graphs(args->nGpus);
std::vector<cudaGraphExec_t> graphExec(args->nGpus);
2021-06-28 14:19:45 -07:00
if (cudaGraphLaunches >= 1) {
// Begin cuda graph capture
for (int i=0; i<args->nGpus; i++) {
2022-08-19 15:15:10 -05:00
// Thread local mdoe is needed for:
// - Multi-thread mode: where graph capture and instantiation can happen concurrently across threads
// - P2P pre-connect: when there is no warm-up, P2P pre-connect is done during graph capture.
2024-03-05 09:47:18 -07:00
// Since pre-connect calls cudaMalloc, we cannot use global capture mode
CUDACHECK(cudaStreamBeginCapture(args->streams[i], cudaStreamCaptureModeThreadLocal));
2021-06-28 14:19:45 -07:00
}
}
2021-07-13 10:17:05 -07:00
#endif
2021-06-28 14:19:45 -07:00
2017-08-08 16:18:34 -07:00
// Performance Benchmark
2022-09-20 02:21:36 -07:00
timer tim;
2017-08-08 16:18:34 -07:00
for (int iter = 0; iter < iters; iter++) {
2019-03-06 18:17:20 -08:00
if (agg_iters>1) NCCLCHECK(ncclGroupStart());
for (int aiter = 0; aiter < agg_iters; aiter++) {
TESTCHECK(startColl(args, type, op, root, in_place, iter*agg_iters+aiter));
}
if (agg_iters>1) NCCLCHECK(ncclGroupEnd());
2017-08-08 16:18:34 -07:00
}
2021-06-28 14:19:45 -07:00
2022-08-09 16:45:27 -06:00
#if HIP_VERSION >= 50221310
2021-06-28 14:19:45 -07:00
if (cudaGraphLaunches >= 1) {
// End cuda graph capture
for (int i=0; i<args->nGpus; i++) {
2025-04-11 12:00:15 -05:00
CUDACHECK(cudaStreamEndCapture(args->streams[i], graphs.data()+i));
2021-06-28 14:19:45 -07:00
}
// Instantiate cuda graph
for (int i=0; i<args->nGpus; i++) {
2025-04-11 12:00:15 -05:00
CUDACHECK(cudaGraphInstantiate(graphExec.data()+i, graphs[i], NULL, NULL, 0));
2021-06-28 14:19:45 -07:00
}
// Resync CPU, restart timing, launch cuda graph
Barrier(args);
2022-09-20 02:21:36 -07:00
tim.reset();
2021-06-28 14:19:45 -07:00
for (int l=0; l<cudaGraphLaunches; l++) {
for (int i=0; i<args->nGpus; i++) {
2024-03-05 09:47:18 -07:00
CUDACHECK(cudaGraphLaunch(graphExec[i], args->streams[i]));
2021-06-28 14:19:45 -07:00
}
}
}
2021-07-13 10:17:05 -07:00
#endif
2021-06-28 14:19:45 -07:00
2022-09-20 02:21:36 -07:00
double cputimeSec = tim.elapsed()/(iters*agg_iters);
2019-03-06 18:17:20 -08:00
TESTCHECK(completeColl(args));
2017-08-08 16:18:34 -07:00
2022-09-20 02:21:36 -07:00
double deltaSec = tim.elapsed();
2019-03-06 18:17:20 -08:00
deltaSec = deltaSec/(iters*agg_iters);
2021-06-28 14:19:45 -07:00
if (cudaGraphLaunches >= 1) deltaSec = deltaSec/cudaGraphLaunches;
2021-07-08 16:42:40 -07:00
Allreduce(args, &deltaSec, average);
2021-06-28 14:19:45 -07:00
2022-08-09 16:45:27 -06:00
#if HIP_VERSION >= 50221310
2021-06-28 14:19:45 -07:00
if (cudaGraphLaunches >= 1) {
//destroy cuda graph
for (int i=0; i<args->nGpus; i++) {
2024-03-05 09:47:18 -07:00
CUDACHECK(cudaGraphExecDestroy(graphExec[i]));
CUDACHECK(cudaGraphDestroy(graphs[i]));
2021-06-28 14:19:45 -07:00
}
}
2021-07-13 10:17:05 -07:00
#endif
2017-08-08 16:18:34 -07:00
double algBw, busBw;
2019-03-06 18:17:20 -08:00
args->collTest->getBw(count, wordSize(type), deltaSec, &algBw, &busBw, args->nProcs*args->nThreads*args->nGpus);
2017-08-08 16:18:34 -07:00
Barrier(args);
2022-08-19 15:15:10 -05:00
int64_t wrongElts = 0;
2019-03-06 18:17:20 -08:00
static __thread int rep = 0;
rep++;
2023-09-13 11:15:13 -07:00
for (int c = 0; c < datacheck; c++) {
2019-03-06 18:17:20 -08:00
// Initialize sendbuffs, recvbuffs and expected
TESTCHECK(args->collTest->initData(args, type, op, root, rep, in_place));
2017-08-08 16:18:34 -07:00
2022-08-09 16:45:27 -06:00
#if HIP_VERSION >= 50221310
2021-06-28 14:19:45 -07:00
if (cudaGraphLaunches >= 1) {
// Begin cuda graph capture for data check
for (int i=0; i<args->nGpus; i++) {
2024-03-05 09:47:18 -07:00
CUDACHECK(cudaStreamBeginCapture(args->streams[i], args->nThreads > 1 ? cudaStreamCaptureModeThreadLocal : cudaStreamCaptureModeGlobal));
2021-06-28 14:19:45 -07:00
}
}
2021-07-13 10:17:05 -07:00
#endif
2021-06-28 14:19:45 -07:00
2019-03-06 18:17:20 -08:00
//test validation in single itertion, should ideally be included into the multi-iteration run
TESTCHECK(startColl(args, type, op, root, in_place, 0));
2017-08-08 16:18:34 -07:00
2022-08-09 16:45:27 -06:00
#if HIP_VERSION >= 50221310
2021-06-28 14:19:45 -07:00
if (cudaGraphLaunches >= 1) {
// End cuda graph capture
for (int i=0; i<args->nGpus; i++) {
2025-04-11 12:00:15 -05:00
CUDACHECK(cudaStreamEndCapture(args->streams[i], graphs.data()+i));
2021-06-28 14:19:45 -07:00
}
// Instantiate cuda graph
for (int i=0; i<args->nGpus; i++) {
2025-04-11 12:00:15 -05:00
CUDACHECK(cudaGraphInstantiate(graphExec.data()+i, graphs[i], NULL, NULL, 0));
2021-06-28 14:19:45 -07:00
}
// Launch cuda graph
for (int i=0; i<args->nGpus; i++) {
2024-03-05 09:47:18 -07:00
CUDACHECK(cudaGraphLaunch(graphExec[i], args->streams[i]));
2021-06-28 14:19:45 -07:00
}
}
2021-07-13 10:17:05 -07:00
#endif
2021-06-28 14:19:45 -07:00
2019-03-06 18:17:20 -08:00
TESTCHECK(completeColl(args));
2017-08-08 16:18:34 -07:00
2022-08-09 16:45:27 -06:00
#if HIP_VERSION >= 50221310
2021-06-28 14:19:45 -07:00
if (cudaGraphLaunches >= 1) {
//destroy cuda graph
for (int i=0; i<args->nGpus; i++) {
2024-03-05 09:47:18 -07:00
CUDACHECK(cudaGraphExecDestroy(graphExec[i]));
CUDACHECK(cudaGraphDestroy(graphs[i]));
2019-03-06 18:17:20 -08:00
}
}
2021-07-13 10:17:05 -07:00
#endif
2021-06-28 14:19:45 -07:00
2022-08-19 15:15:10 -05:00
TESTCHECK(CheckData(args, type, op, root, in_place, &wrongElts));
2017-08-08 16:18:34 -07:00
2019-03-06 18:17:20 -08:00
//aggregate delta from all threads and procs
2022-08-19 15:15:10 -05:00
long long wrongElts1 = wrongElts;
2023-09-13 11:15:13 -07:00
//if (wrongElts) fprintf(stderr, "\nERROR: Data corruption : rank %d size %ld wrongElts %ld\n", args->proc, args->expectedBytes, wrongElts);
2022-08-19 15:15:10 -05:00
Allreduce(args, &wrongElts1, /*sum*/4);
wrongElts = wrongElts1;
2023-09-13 11:15:13 -07:00
if (wrongElts) break;
2017-08-08 16:18:34 -07:00
}
2022-09-20 02:21:36 -07:00
double timeUsec = (report_cputime ? cputimeSec : deltaSec)*1.0E6;
2021-06-28 18:23:12 -07:00
char timeStr[100];
2021-07-08 16:42:40 -07:00
if (timeUsec >= 10000.0) {
2019-03-06 18:17:20 -08:00
sprintf(timeStr, "%7.0f", timeUsec);
2021-06-28 18:23:12 -07:00
} else if (timeUsec >= 100.0) {
2019-03-06 18:17:20 -08:00
sprintf(timeStr, "%7.1f", timeUsec);
} else {
sprintf(timeStr, "%7.2f", timeUsec);
}
2022-09-06 13:17:15 -07:00
if (args->reportErrors) {
2022-08-19 15:15:10 -05:00
PRINT(" %7s %6.2f %6.2f %5g", timeStr, algBw, busBw, (double)wrongElts);
2017-08-08 16:18:34 -07:00
} else {
2022-08-19 15:15:10 -05:00
PRINT(" %7s %6.2f %6.2f %5s", timeStr, algBw, busBw, "N/A");
2017-08-08 16:18:34 -07:00
}
2025-04-25 06:05:21 -10:00
auto largestMessageSize = std::max(args->sendBytes, args->expectedBytes);
2025-01-13 15:28:29 -08:00
if (args->reporter) {
if (args->reportErrors) {
2025-04-25 06:05:21 -10:00
args->reporter->addResult((args->nThreads * args->nGpus), args->nProcs, args->totalProcs, largestMessageSize, in_place, timeUsec, algBw, busBw, wrongElts);
} else {
args->reporter->addResult((args->nThreads * args->nGpus), args->nProcs, args->totalProcs, largestMessageSize, in_place, timeUsec, algBw, busBw);
2025-01-13 15:28:29 -08:00
}
}
2017-08-08 16:18:34 -07:00
args->bw[0] += busBw;
args->bw_count[0]++;
2019-03-06 18:17:20 -08:00
return testSuccess;
2017-08-08 16:18:34 -07:00
}
2019-03-06 18:17:20 -08:00
void setupArgs(size_t size, ncclDataType_t type, struct threadArgs* args) {
2017-08-08 16:18:34 -07:00
int nranks = args->nProcs*args->nGpus*args->nThreads;
2019-03-06 18:17:20 -08:00
size_t count, sendCount, recvCount, paramCount, sendInplaceOffset, recvInplaceOffset;
2017-08-08 16:18:34 -07:00
count = size / wordSize(type);
2024-12-18 11:14:18 -08:00
args->collTest->getCollByteCount(&sendCount, &recvCount, ¶mCount, &sendInplaceOffset, &recvInplaceOffset, (size_t)count, wordSize(type), (size_t)nranks);
2017-08-08 16:18:34 -07:00
args->nbytes = paramCount * wordSize(type);
args->sendBytes = sendCount * wordSize(type);
args->expectedBytes = recvCount * wordSize(type);
args->sendInplaceOffset = sendInplaceOffset * wordSize(type);
args->recvInplaceOffset = recvInplaceOffset * wordSize(type);
}
2019-03-06 18:17:20 -08:00
testResult_t TimeTest(struct threadArgs* args, ncclDataType_t type, const char* typeName, ncclRedOp_t op, const char* opName, int root) {
2022-09-20 02:21:36 -07:00
// Sync to avoid first-call timeout
Barrier(args);
2019-03-06 18:17:20 -08:00
// Warm-up for large size
2017-08-08 16:18:34 -07:00
setupArgs(args->maxbytes, type, args);
2024-05-01 20:41:12 -05:00
#if HIP_VERSION >= 50221310
2025-04-11 12:00:15 -05:00
std::vector<cudaGraph_t> graphs(args->nGpus);
std::vector<cudaGraphExec_t> graphExec(args->nGpus);
2024-05-01 20:41:12 -05:00
if (cudaGraphLaunches >= 1) {
// Begin cuda graph capture
for (int i=0; i<args->nGpus; i++) {
2024-05-02 09:18:25 -06:00
// Thread local mode is needed for:
2024-05-01 20:41:12 -05:00
// - Multi-thread mode: where graph capture and instantiation can happen concurrently across threads
// - P2P pre-connect: when there is no warm-up, P2P pre-connect is done during graph capture.
// Since pre-connect calls cudaMalloc, we cannot use global capture mode
CUDACHECK(cudaStreamBeginCapture(args->streams[i], cudaStreamCaptureModeThreadLocal));
}
}
#endif
2017-08-08 16:18:34 -07:00
for (int iter = 0; iter < warmup_iters; iter++) {
2019-03-06 18:17:20 -08:00
TESTCHECK(startColl(args, type, op, root, 0, iter));
}
2024-05-01 20:41:12 -05:00
#if HIP_VERSION >= 50221310
if (cudaGraphLaunches >= 1) {
// End cuda graph capture
for (int i=0; i<args->nGpus; i++) {
2025-04-11 12:00:15 -05:00
CUDACHECK(cudaStreamEndCapture(args->streams[i], graphs.data()+i));
2024-05-01 20:41:12 -05:00
}
// Instantiate cuda graph
for (int i=0; i<args->nGpus; i++) {
2025-04-11 12:00:15 -05:00
CUDACHECK(cudaGraphInstantiate(graphExec.data()+i, graphs[i], NULL, NULL, 0));
2024-05-01 20:41:12 -05:00
}
// Resync CPU, restart timing, launch cuda graph
Barrier(args);
for (int l=0; l<cudaGraphLaunches; l++) {
for (int i=0; i<args->nGpus; i++) {
CUDACHECK(cudaGraphLaunch(graphExec[i], args->streams[i]));
}
}
}
#endif
2019-03-06 18:17:20 -08:00
TESTCHECK(completeColl(args));
2024-05-01 20:41:12 -05:00
#if HIP_VERSION >= 50221310
if (cudaGraphLaunches >= 1) {
//destroy cuda graph
for (int i=0; i<args->nGpus; i++) {
CUDACHECK(cudaGraphExecDestroy(graphExec[i]));
CUDACHECK(cudaGraphDestroy(graphs[i]));
}
}
#endif
2019-03-06 18:17:20 -08:00
// Warm-up for small size
setupArgs(args->minbytes, type, args);
2024-05-01 20:41:12 -05:00
#if HIP_VERSION >= 50221310
if (cudaGraphLaunches >= 1) {
// Begin cuda graph capture
for (int i=0; i<args->nGpus; i++) {
2024-05-02 09:18:25 -06:00
// Thread local mode is needed for:
2024-05-01 20:41:12 -05:00
// - Multi-thread mode: where graph capture and instantiation can happen concurrently across threads
// - P2P pre-connect: when there is no warm-up, P2P pre-connect is done during graph capture.
// Since pre-connect calls cudaMalloc, we cannot use global capture mode
CUDACHECK(cudaStreamBeginCapture(args->streams[i], cudaStreamCaptureModeThreadLocal));
}
}
#endif
2019-03-06 18:17:20 -08:00
for (int iter = 0; iter < warmup_iters; iter++) {
2023-10-16 12:13:50 -07:00
TESTCHECK(startColl(args, type, op, root, iter < warmup_iters/2 ? 0 : 1, iter));
2017-08-08 16:18:34 -07:00
}
2024-05-01 20:41:12 -05:00
#if HIP_VERSION >= 50221310
if (cudaGraphLaunches >= 1) {
// End cuda graph capture
for (int i=0; i<args->nGpus; i++) {
2025-04-11 12:00:15 -05:00
CUDACHECK(cudaStreamEndCapture(args->streams[i], graphs.data()+i));
2024-05-01 20:41:12 -05:00
}
// Instantiate cuda graph
for (int i=0; i<args->nGpus; i++) {
2025-04-11 12:00:15 -05:00
CUDACHECK(cudaGraphInstantiate(graphExec.data()+i, graphs[i], NULL, NULL, 0));
2024-05-01 20:41:12 -05:00
}
// Resync CPU, restart timing, launch cuda graph
Barrier(args);
for (int l=0; l<cudaGraphLaunches; l++) {
for (int i=0; i<args->nGpus; i++) {
CUDACHECK(cudaGraphLaunch(graphExec[i], args->streams[i]));
}
}
}
#endif
2019-03-06 18:17:20 -08:00
TESTCHECK(completeColl(args));
2017-08-08 16:18:34 -07:00
2024-05-01 20:41:12 -05:00
#if HIP_VERSION >= 50221310
if (cudaGraphLaunches >= 1) {
//destroy cuda graph
for (int i=0; i<args->nGpus; i++) {
CUDACHECK(cudaGraphExecDestroy(graphExec[i]));
CUDACHECK(cudaGraphDestroy(graphs[i]));
}
}
#endif
2017-08-08 16:18:34 -07:00
// Benchmark
2024-07-25 21:47:40 -07:00
long repeat = run_cycles;
2025-04-11 12:00:15 -05:00
size_t iter = 0;
2025-01-13 15:28:29 -08:00
2024-07-25 21:47:40 -07:00
do {
2025-04-11 12:00:15 -05:00
if (run_cycles > 1) PRINT("# Testing %lu cycle.\n", iter+1);
if (args->reporter) {
args->reporter->setParameters(iter, args->collTest->name, typeName, opName);
}
2020-03-11 13:40:17 -07:00
for (size_t size = args->minbytes; size<=args->maxbytes; size = ((args->stepfactor > 1) ? size*args->stepfactor : size+args->stepbytes)) {
2017-08-08 16:18:34 -07:00
setupArgs(size, type, args);
2022-08-19 15:15:10 -05:00
char rootName[100];
sprintf(rootName, "%6i", root);
2025-04-11 12:00:15 -05:00
PRINT("%12li %12li %8s %6s %6s", std::max(args->sendBytes, args->expectedBytes), args->nbytes / wordSize(type), typeName, opName, rootName);
if (enable_out_of_place) {
TESTCHECK(BenchTime(args, type, op, root, 0));
usleep(delay_inout_place);
}
2025-04-03 17:31:54 -05:00
if (enable_in_place)
TESTCHECK(BenchTime(args, type, op, root, 1));
2017-08-08 16:18:34 -07:00
PRINT("\n");
2020-03-11 13:40:17 -07:00
}
2025-04-11 12:00:15 -05:00
--repeat;
++iter;
} while(repeat != 0);
2024-07-25 21:47:40 -07:00
2019-03-06 18:17:20 -08:00
return testSuccess;
2017-08-08 16:18:34 -07:00
}
2019-03-06 18:17:20 -08:00
testResult_t threadRunTests(struct threadArgs* args) {
2017-08-08 16:18:34 -07:00
// Set device to the first of our GPUs. If we don't do that, some operations
// will be done on the current GPU (by default : 0) and if the GPUs are in
// exclusive mode those operations will fail.
2024-03-05 09:47:18 -07:00
CUDACHECK(cudaSetDevice(args->gpus[0]));
2019-03-06 18:17:20 -08:00
TESTCHECK(ncclTestEngine.runTest(args, ncclroot, (ncclDataType_t)nccltype, test_typenames[nccltype], (ncclRedOp_t)ncclop, test_opnames[ncclop]));
return testSuccess;
2017-08-08 16:18:34 -07:00
}
2019-03-06 18:17:20 -08:00
testResult_t threadInit(struct threadArgs* args) {
2017-08-08 16:18:34 -07:00
char hostname[1024];
getHostName(hostname, 1024);
2019-03-06 18:17:20 -08:00
int nranks = args->nProcs*args->nThreads*args->nGpus;
2017-08-08 16:18:34 -07:00
//set main thread again
2022-09-20 02:21:36 -07:00
is_main_thread = (is_main_proc && args->thread == 0) ? 1 : 0;
2017-08-08 16:18:34 -07:00
NCCLCHECK(ncclGroupStart());
2019-03-06 18:17:20 -08:00
for (int i=0; i<args->nGpus; i++) {
int rank = args->proc*args->nThreads*args->nGpus + args->thread*args->nGpus + i;
2024-03-05 09:47:18 -07:00
CUDACHECK(cudaSetDevice(args->gpus[i]));
2019-03-06 18:17:20 -08:00
NCCLCHECK(ncclCommInitRank(args->comms+i, nranks, args->ncclId, rank));
2017-08-08 16:18:34 -07:00
}
NCCLCHECK(ncclGroupEnd());
2024-02-28 05:18:40 -08:00
#if NCCL_VERSION_CODE >= NCCL_VERSION(2,19,0)
2025-06-03 10:36:53 -07:00
NCCLCHECK(ncclGroupStart());
2024-02-28 05:18:40 -08:00
void **sendRegHandles = (local_register) ? (void **)malloc(sizeof(*sendRegHandles)*args->nGpus) : NULL;
void **recvRegHandles = (local_register) ? (void **)malloc(sizeof(*recvRegHandles)*args->nGpus) : NULL;
for (int i=0; i<args->nGpus; i++) {
2025-05-19 18:20:22 -07:00
#if NCCL_VERSION_CODE >= NCCL_VERSION(2,27,0)
if (test_ncclVersion >= NCCL_VERSION(2,27,0) && (local_register == SYMMETRIC_REGISTER)) {
NCCLCHECK(ncclCommWindowRegister(args->comms[i], args->sendbuffs[i], args->maxbytes, (ncclWindow_t*)&sendRegHandles[i], NCCL_WIN_COLL_SYMMETRIC));
NCCLCHECK(ncclCommWindowRegister(args->comms[i], args->recvbuffs[i], args->maxbytes, (ncclWindow_t*)&recvRegHandles[i], NCCL_WIN_COLL_SYMMETRIC));
} else
#endif
{
if (local_register) NCCLCHECK(ncclCommRegister(args->comms[i], args->sendbuffs[i], args->maxbytes, &sendRegHandles[i]));
if (local_register) NCCLCHECK(ncclCommRegister(args->comms[i], args->recvbuffs[i], args->maxbytes, &recvRegHandles[i]));
}
2024-02-28 05:18:40 -08:00
}
2025-06-03 10:36:53 -07:00
NCCLCHECK(ncclGroupEnd());
2024-02-28 05:18:40 -08:00
#endif
2017-08-08 16:18:34 -07:00
2019-03-06 18:17:20 -08:00
TESTCHECK(threadRunTests(args));
2017-08-08 16:18:34 -07:00
2019-03-06 18:17:20 -08:00
for (int i=0; i<args->nGpus; i++) {
2024-02-28 05:18:40 -08:00
#if NCCL_VERSION_CODE >= NCCL_VERSION(2,19,0)
2025-05-19 18:20:22 -07:00
#if NCCL_VERSION_CODE >= NCCL_VERSION(2,27,0)
if (test_ncclVersion >= NCCL_VERSION(2,27,0) && (local_register == SYMMETRIC_REGISTER)) {
NCCLCHECK(ncclCommWindowDeregister(args->comms[i], (ncclWindow_t)sendRegHandles[i]));
NCCLCHECK(ncclCommWindowDeregister(args->comms[i], (ncclWindow_t)recvRegHandles[i]));
} else
#endif
{
if (local_register) NCCLCHECK(ncclCommDeregister(args->comms[i], sendRegHandles[i]));
if (local_register) NCCLCHECK(ncclCommDeregister(args->comms[i], recvRegHandles[i]));
}
2024-02-28 05:18:40 -08:00
#endif
2019-03-06 18:17:20 -08:00
NCCLCHECK(ncclCommDestroy(args->comms[i]));
}
return testSuccess;
2017-08-08 16:18:34 -07:00
}
2019-03-06 18:17:20 -08:00
void* threadLauncher(void* thread_) {
struct testThread* thread = (struct testThread*)thread_;
thread->ret = thread->func(&thread->args);
return NULL;
2017-08-08 16:18:34 -07:00
}
2019-03-06 18:17:20 -08:00
testResult_t threadLaunch(struct testThread* thread) {
pthread_create(&thread->thread, NULL, threadLauncher, thread);
return testSuccess;
2017-08-08 16:18:34 -07:00
}
2022-10-17 14:13:48 +00:00
testResult_t AllocateBuffs(void **sendbuff, size_t sendBytes, void **recvbuff, size_t recvBytes, void **expected, size_t nbytes) {
2024-06-04 11:35:39 -05:00
if(enable_rotating_tensor) {
recvBytes = recvBytes + cache_bytes;
nbytes = nbytes + cache_bytes;
}
2019-05-01 12:58:04 -07:00
if (memorytype == ncclFine) {
2025-01-11 19:23:04 -06:00
if(HIP_VERSION >= 50700000) {
CUDACHECK(hipExtMallocWithFlags(sendbuff, nbytes, hipDeviceMallocUncached));
CUDACHECK(hipExtMallocWithFlags(recvbuff, nbytes, hipDeviceMallocUncached));
if (datacheck) CUDACHECK(hipExtMallocWithFlags(expected, recvBytes, hipDeviceMallocUncached));
}
else {
CUDACHECK(hipExtMallocWithFlags(sendbuff, nbytes, hipDeviceMallocFinegrained));
CUDACHECK(hipExtMallocWithFlags(recvbuff, nbytes, hipDeviceMallocFinegrained));
if (datacheck) CUDACHECK(hipExtMallocWithFlags(expected, recvBytes, hipDeviceMallocFinegrained));
}
2019-05-01 12:58:04 -07:00
}
else if (memorytype == ncclHost) {
2024-03-05 09:47:18 -07:00
CUDACHECK(hipHostMalloc(sendbuff, nbytes));
CUDACHECK(hipHostMalloc(recvbuff, nbytes));
if (datacheck) CUDACHECK(hipHostMalloc(expected, recvBytes));
2019-05-01 12:58:04 -07:00
}
2020-12-15 22:05:50 -05:00
else if (memorytype == ncclManaged) {
2024-03-05 09:47:18 -07:00
CUDACHECK(cudaMallocManaged(sendbuff, nbytes));
CUDACHECK(cudaMallocManaged(recvbuff, nbytes));
if (datacheck) CUDACHECK(cudaMallocManaged(expected, recvBytes));
2021-01-04 16:51:16 -05:00
#if 0
2024-03-05 09:47:18 -07:00
CUDACHECK(cudaMemset(*sendbuff, 0, nbytes));
CUDACHECK(cudaMemset(*recvbuff, 0, nbytes));
if (datacheck) CUDACHECK(cudaMemset(*expected, 0, recvBytes));
2021-01-04 16:51:16 -05:00
#endif
2020-12-15 22:05:50 -05:00
}
2019-05-01 12:58:04 -07:00
else {
2025-07-24 11:14:49 -04:00
#if NCCL_VERSION_CODE >= NCCL_VERSION(2,19,0)
NCCLCHECK(ncclMemAlloc(sendbuff, nbytes));
NCCLCHECK(ncclMemAlloc(recvbuff, nbytes));
if (datacheck) NCCLCHECK(ncclMemAlloc(expected, recvBytes));
#else
2024-03-05 09:47:18 -07:00
CUDACHECK(cudaMalloc(sendbuff, nbytes));
CUDACHECK(cudaMalloc(recvbuff, nbytes));
if (datacheck) CUDACHECK(cudaMalloc(expected, recvBytes));
2025-07-24 11:14:49 -04:00
#endif
2019-05-01 12:58:04 -07:00
}
2025-01-11 17:24:17 -05:00
CUDACHECK(hipMemset(*sendbuff, 1, nbytes));
if (datacheck) CUDACHECK(hipMemset(*expected, 1, recvBytes));
2019-05-01 12:58:04 -07:00
return testSuccess;
2017-08-08 16:18:34 -07:00
}
2019-03-06 18:17:20 -08:00
testResult_t run(); // Main function
2017-08-08 16:18:34 -07:00
int main(int argc, char* argv[]) {
2019-03-06 18:17:20 -08:00
// Make sure everyline is flushed so that we see the progress of the test
setlinebuf(stdout);
2021-06-17 14:08:43 -07:00
#if NCCL_VERSION_CODE >= NCCL_VERSION(2,4,0)
ncclGetVersion(&test_ncclVersion);
#else
test_ncclVersion = NCCL_VERSION_CODE;
#endif
//printf("# NCCL_VERSION_CODE=%d ncclGetVersion=%d\n", NCCL_VERSION_CODE, test_ncclVersion);
2021-09-13 14:43:22 -07:00
#if NCCL_VERSION_CODE >= NCCL_VERSION(2,0,0)
test_opnum = 4;
test_typenum = 9;
if (NCCL_VERSION_CODE >= NCCL_VERSION(2,10,0) && test_ncclVersion >= NCCL_VERSION(2,10,0)) {
test_opnum++; // ncclAvg
}
if (NCCL_VERSION_CODE >= NCCL_VERSION(2,11,0) && test_ncclVersion >= NCCL_VERSION(2,11,0)) {
test_opnum++; // PreMulSum
}
2025-07-23 14:22:18 -05:00
#if defined(RCCL_BFLOAT16)
2025-04-18 19:20:59 -07:00
if (NCCL_VERSION_CODE >= NCCL_VERSION(2,10,0) && test_ncclVersion >= NCCL_VERSION(2,10,0)) {
test_typenum++; // bfloat16
}
#endif
2025-07-23 14:22:18 -05:00
#if defined(RCCL_FLOAT8)
if (NCCL_VERSION_CODE >= NCCL_VERSION(2,10,0) && test_ncclVersion >= NCCL_VERSION(2,10,0)) {
2025-04-18 19:20:59 -07:00
test_typenum += 2; // fp8 e4m3,e5m2
}
#endif
2021-09-13 14:43:22 -07:00
#endif
2021-06-17 14:08:43 -07:00
2019-03-06 18:17:20 -08:00
// Parse args
2021-06-02 17:52:11 -07:00
double parsed;
2019-03-06 18:17:20 -08:00
int longindex;
static struct option longopts[] = {
{"nthreads", required_argument, 0, 't'},
{"ngpus", required_argument, 0, 'g'},
{"minbytes", required_argument, 0, 'b'},
{"maxbytes", required_argument, 0, 'e'},
2017-08-08 16:18:34 -07:00
{"stepbytes", required_argument, 0, 'i'},
{"stepfactor", required_argument, 0, 'f'},
{"iters", required_argument, 0, 'n'},
2019-03-06 18:17:20 -08:00
{"agg_iters", required_argument, 0, 'm'},
2017-08-08 16:18:34 -07:00
{"warmup_iters", required_argument, 0, 'w'},
2024-07-25 21:47:40 -07:00
{"run_cycles", required_argument, 0, 'N'},
2017-08-08 16:18:34 -07:00
{"parallel_init", required_argument, 0, 'p'},
{"check", required_argument, 0, 'c'},
{"op", required_argument, 0, 'o'},
{"datatype", required_argument, 0, 'd'},
{"root", required_argument, 0, 'r'},
2019-03-06 18:17:20 -08:00
{"blocking", required_argument, 0, 'z'},
2022-09-20 02:21:36 -07:00
{"stream_null", required_argument, 0, 'y'},
{"timeout", required_argument, 0, 'T'},
2021-06-28 14:19:45 -07:00
{"cudagraph", required_argument, 0, 'G'},
2022-09-20 02:21:36 -07:00
{"report_cputime", required_argument, 0, 'C'},
2021-06-30 19:36:07 -07:00
{"average", required_argument, 0, 'a'},
2024-02-28 05:18:40 -08:00
{"local_register", required_argument, 0, 'R'},
2025-04-11 12:00:15 -05:00
{"memory_type", required_argument, 0, 'y'}, //RCCL
{"cumask", required_argument, 0, 'u'}, //RCCL
{"out_of_place", required_argument, 0, 'O'}, //RCCL
{"delay_inout_place", required_argument, 0, 'q'}, //RCCL
{"cache_flush", required_argument, 0, 'F'}, //RCCL
{"rotating_tensor", required_argument, 0, 'E'}, //RCCL
{"output_file", required_argument, 0, 'x'}, //RCCL
{"output_format", required_argument, 0, 'Z'}, //RCCL
2021-09-13 14:43:22 -07:00
{"help", no_argument, 0, 'h'},
{}
2019-03-06 18:17:20 -08:00
};
while(1) {
int c;
2022-10-21 17:12:45 -05:00
2025-04-11 12:00:15 -05:00
c = getopt_long(argc, argv, "t:g:b:e:i:f:n:m:w:N:p:c:o:d:r:z:y:T:G:C:a:R:Y:u:O:q:F:E:x:Z:h", longopts, &longindex);
2019-03-06 18:17:20 -08:00
if (c == -1)
break;
switch(c) {
case 't':
nThreads = strtol(optarg, NULL, 0);
break;
case 'g':
nGpus = strtol(optarg, NULL, 0);
break;
case 'b':
2021-06-02 17:52:11 -07:00
parsed = parsesize(optarg);
if (parsed < 0) {
fprintf(stderr, "invalid size specified for 'minbytes'\n");
return -1;
}
minBytes = (size_t)parsed;
2019-03-06 18:17:20 -08:00
break;
case 'e':
2021-06-02 17:52:11 -07:00
parsed = parsesize(optarg);
if (parsed < 0) {
fprintf(stderr, "invalid size specified for 'maxbytes'\n");
return -1;
}
maxBytes = (size_t)parsed;
2019-03-06 18:17:20 -08:00
break;
case 'i':
2024-06-14 11:28:55 +02:00
parsed = parsesize(optarg);
if (parsed < 0) {
fprintf(stderr, "invalid size specified for 'stepBytes'\n");
return -1;
}
stepBytes = (size_t)parsed;
2019-03-06 18:17:20 -08:00
break;
case 'f':
stepFactor = strtol(optarg, NULL, 0);
break;
case 'n':
iters = (int)strtol(optarg, NULL, 0);
break;
case 'm':
2021-06-17 14:08:43 -07:00
#if NCCL_MAJOR > 2 || (NCCL_MAJOR >= 2 && NCCL_MINOR >= 2)
2019-03-06 18:17:20 -08:00
agg_iters = (int)strtol(optarg, NULL, 0);
#else
2021-06-02 17:52:11 -07:00
fprintf(stderr, "Option -m not supported before NCCL 2.2. Ignoring\n");
2019-03-06 18:17:20 -08:00
#endif
break;
case 'w':
warmup_iters = (int)strtol(optarg, NULL, 0);
break;
2024-07-25 21:47:40 -07:00
case 'N':
run_cycles = (int)strtol(optarg, NULL, 0);
2019-03-06 18:17:20 -08:00
break;
case 'p':
parallel_init = (int)strtol(optarg, NULL, 0);
break;
2025-04-11 12:00:15 -05:00
case 'c':
datacheck = (int)strtol(optarg, NULL, 0);
break;
2019-03-06 18:17:20 -08:00
case 'o':
ncclop = ncclstringtoop(optarg);
break;
case 'd':
nccltype = ncclstringtotype(optarg);
break;
case 'r':
2024-06-14 11:46:08 -05:00
ncclroot = ncclstringtoroot(optarg);
2019-03-06 18:17:20 -08:00
break;
case 'z':
blocking_coll = strtol(optarg, NULL, 0);
break;
2022-09-20 02:21:36 -07:00
case 'y':
streamnull = strtol(optarg, NULL, 0);
break;
case 'T':
timeout = strtol(optarg, NULL, 0);
2020-08-21 21:34:55 +00:00
break;
2021-06-28 14:19:45 -07:00
case 'G':
2022-08-09 16:45:27 -06:00
#if (NCCL_MAJOR > 2 || (NCCL_MAJOR >= 2 && NCCL_MINOR >= 9)) && HIP_VERSION >= 50221310
2021-06-28 14:19:45 -07:00
cudaGraphLaunches = strtol(optarg, NULL, 0);
#else
2022-08-09 16:45:27 -06:00
printf("Option -G (HIP graph) not supported before NCCL 2.9 + ROCm 5.2 Ignoring\n");
2021-06-28 14:19:45 -07:00
#endif
break;
2022-09-20 02:21:36 -07:00
case 'C':
report_cputime = strtol(optarg, NULL, 0);
break;
2021-06-30 19:36:07 -07:00
case 'a':
average = (int)strtol(optarg, NULL, 0);
break;
2024-02-28 05:18:40 -08:00
case 'R':
#if NCCL_VERSION_CODE >= NCCL_VERSION(2,19,0)
2025-05-19 18:20:22 -07:00
local_register = (int)strtol(optarg, NULL, 0);
if (local_register == SYMMETRIC_REGISTER && test_ncclVersion < NCCL_VERSION(2,27,0)) {
printf("Option -R 2 (symmetric) is not supported before NCCL 2.27. Defaulting to local registration\n");
local_register = LOCAL_REGISTER;
2024-02-28 05:18:40 -08:00
}
#else
printf("Option -R (register) is not supported before NCCL 2.19. Ignoring\n");
#endif
break;
2025-04-11 12:00:15 -05:00
case 'Y':
memorytype = ncclstringtomtype(optarg);
break;
case 'u':
{
int nmasks = 0;
char *mask = strtok(optarg, ",");
while (mask != NULL && nmasks < 4) {
cumask[nmasks++] = strtol(mask, NULL, 16);
mask = strtok(NULL, ",");
};
}
break;
2024-01-04 16:20:42 -06:00
case 'O':
enable_out_of_place = strtol(optarg, NULL, 0);
2025-04-03 17:31:54 -05:00
enable_in_place = enable_out_of_place ? 0 : 1;
2024-01-04 16:20:42 -06:00
break;
2025-04-11 12:00:15 -05:00
case 'q':
delay_inout_place = (int)strtol(optarg, NULL, 10);
2025-04-03 17:31:54 -05:00
break;
2024-05-07 11:09:32 -05:00
case 'F':
enable_cache_flush = strtol(optarg, NULL, 0);
2024-05-10 08:46:13 -07:00
if (enable_cache_flush > 0) {
hipDeviceProp_t deviceProps;
CHECK_HIP_ERROR(hipGetDeviceProperties(&deviceProps, 0));
gpu_block3 = deviceProps.multiProcessorCount * 60;
}
2024-05-07 11:09:32 -05:00
break;
2024-07-31 14:57:20 +00:00
case 'E':
2024-06-04 11:35:39 -05:00
enable_rotating_tensor = strtol(optarg, NULL, 0);
break;
2025-01-13 15:28:29 -08:00
case 'x':
output_file = optarg;
break;
case 'Z':
output_format = optarg;
break;
2019-03-06 18:17:20 -08:00
case 'h':
default:
2021-06-17 14:08:43 -07:00
if (c != 'h') printf("invalid option '%c'\n", c);
printf("USAGE: %s \n\t"
2019-03-06 18:17:20 -08:00
"[-t,--nthreads <num threads>] \n\t"
"[-g,--ngpus <gpus per thread>] \n\t"
"[-b,--minbytes <min size in bytes>] \n\t"
"[-e,--maxbytes <max size in bytes>] \n\t"
"[-i,--stepbytes <increment size>] \n\t"
"[-f,--stepfactor <increment factor>] \n\t"
"[-n,--iters <iteration count>] \n\t"
"[-m,--agg_iters <aggregated iteration count>] \n\t"
"[-w,--warmup_iters <warmup iteration count>] \n\t"
2024-07-25 21:47:40 -07:00
"[-N,--run_cycles <cycle count> run & print each cycle (default: 1; 0=infinite)] \n\t"
2019-03-06 18:17:20 -08:00
"[-p,--parallel_init <0/1>] \n\t"
2023-09-13 11:15:13 -07:00
"[-c,--check <check iteration count>] \n\t"
2021-09-13 14:43:22 -07:00
#if NCCL_VERSION_CODE >= NCCL_VERSION(2,11,0)
"[-o,--op <sum/prod/min/max/avg/mulsum/all>] \n\t"
#elif NCCL_VERSION_CODE >= NCCL_VERSION(2,10,0)
2021-06-17 14:08:43 -07:00
"[-o,--op <sum/prod/min/max/avg/all>] \n\t"
#else
2019-03-06 18:17:20 -08:00
"[-o,--op <sum/prod/min/max/all>] \n\t"
2021-06-17 14:08:43 -07:00
#endif
2019-03-06 18:17:20 -08:00
"[-d,--datatype <nccltype/all>] \n\t"
2024-06-14 11:46:08 -05:00
"[-r,--root <root/all>] \n\t"
2019-03-06 18:17:20 -08:00
"[-z,--blocking <0/1>] \n\t"
2022-09-20 02:21:36 -07:00
"[-y,--stream_null <0/1>] \n\t"
"[-T,--timeout <time in seconds>] \n\t"
2021-06-28 14:19:45 -07:00
"[-G,--cudagraph <num graph launches>] \n\t"
2022-09-20 02:21:36 -07:00
"[-C,--report_cputime <0/1>] \n\t"
2021-06-30 19:36:07 -07:00
"[-a,--average <0/1/2/3> report average iteration time <0=RANK0/1=AVG/2=MIN/3=MAX>] \n\t"
2025-05-19 18:20:22 -07:00
"[-R,--local_register <0/1/2> enable local (1) or symmetric (2) buffer registration on send/recv buffers (default: disable (0))] \n\t"
2025-04-11 12:00:15 -05:00
"[-Y,--memory_type <coarse/fine/host/managed>] \n\t"
"[-u,--cumask <d0,d1,d2,d3>] \n\t"
2024-01-04 16:20:42 -06:00
"[-O,--out_of_place <0/1>] \n\t"
2025-04-11 12:00:15 -05:00
"[-q,--delay <delay between out-of-place and in-place in microseconds>] \n\t"
2024-07-31 14:57:20 +00:00
"[-F,--cache_flush <number of iterations between instruction cache flush>] \n\t"
"[-E,--rotating_tensor <0/1>] \n\t"
2025-01-13 15:28:29 -08:00
"[-x,--output_file <output file name>] \n\t"
"[-Z,--output_format <output format <csv|json>] \n\t"
2019-03-06 18:17:20 -08:00
"[-h,--help]\n",
2022-09-20 02:21:36 -07:00
basename(argv[0]));
return 0;
2019-03-06 18:17:20 -08:00
}
2017-08-08 16:18:34 -07:00
}
2019-07-11 15:36:21 +00:00
2024-03-05 09:47:18 -07:00
CUDACHECK(cudaGetDeviceCount(&numDevices));
2023-04-27 14:06:17 -04:00
#ifndef MPI_SUPPORT
2019-07-11 15:36:21 +00:00
if (nGpus > numDevices)
{
fprintf(stderr, "[ERROR] The number of requested GPUs (%d) is greater than the number of GPUs available (%d)\n", nGpus, numDevices);
return testNcclError;
}
2023-04-27 14:06:17 -04:00
#endif
2021-06-02 17:52:11 -07:00
if (minBytes > maxBytes) {
fprintf(stderr, "invalid sizes for 'minbytes' and 'maxbytes': %llu > %llu\n",
(unsigned long long)minBytes,
(unsigned long long)maxBytes);
return -1;
}
2025-01-13 15:28:29 -08:00
if (!output_format.empty()) {
if (!(output_format == "csv" || output_format == "json")) {
std::cerr << "Invalid --output_format: " << output_format << "\n";
return -1;
}
}
2019-03-06 18:17:20 -08:00
#ifdef MPI_SUPPORT
MPI_Init(&argc, &argv);
#endif
2021-06-28 18:23:12 -07:00
TESTCHECK(run());
return 0;
2019-03-06 18:17:20 -08:00
}
2017-08-08 16:18:34 -07:00
2025-01-23 11:09:09 -08:00
#ifdef MPI_SUPPORT
// parse int for base 2/10/16, will ignore first whitespaces
static bool parseInt(char *s, int *num) {
char *p = NULL;
if (!s || !num)
return false;
while (*s && isspace(*s)) ++s;
if (!*s) return false;
if (strncasecmp(s, "0b", 2) == 0)
*num = (int)strtoul(s + 2, &p, 2);
else
*num = (int)strtoul(s, &p, 0);
if (p == s)
return false;
return true;
}
#endif
2019-03-06 18:17:20 -08:00
testResult_t run() {
2022-09-20 02:21:36 -07:00
int totalProcs = 1, proc = 0, ncclProcs = 1, ncclProc = 0, color = 0;
2019-03-06 18:17:20 -08:00
int localRank = 0;
2023-04-27 14:06:17 -04:00
int localSize = 0;
2019-03-06 18:17:20 -08:00
char hostname[1024];
getHostName(hostname, 1024);
2017-08-08 16:18:34 -07:00
2025-05-14 15:30:07 -05:00
hipDeviceProp_t devProp;
CUDACHECK(hipGetDeviceProperties(&devProp, 0));
if (IsArchMatch(devProp.gcnArchName, "gfx942")) {
PRINT("On gfx942 architecture, using FNUZ FP8 types");
rccl_float8_useFnuz = true;
}
2017-08-08 16:18:34 -07:00
#ifdef MPI_SUPPORT
2022-09-20 02:21:36 -07:00
MPI_Comm_size(MPI_COMM_WORLD, &totalProcs);
2017-08-08 16:18:34 -07:00
MPI_Comm_rank(MPI_COMM_WORLD, &proc);
2025-04-11 12:00:15 -05:00
std::vector<uint64_t> hostHashs(totalProcs);
2017-08-08 16:18:34 -07:00
hostHashs[proc] = getHostHash(hostname);
2025-04-11 12:00:15 -05:00
MPI_Allgather(MPI_IN_PLACE, 0, MPI_DATATYPE_NULL, hostHashs.data(), sizeof(uint64_t), MPI_BYTE, MPI_COMM_WORLD);
2022-09-20 02:21:36 -07:00
for (int p=0; p<totalProcs; p++) {
2017-08-08 16:18:34 -07:00
if (p == proc) break;
if (hostHashs[p] == hostHashs[proc]) localRank++;
}
2022-09-20 02:21:36 -07:00
2025-01-23 11:09:09 -08:00
char *splitMaskEnv = NULL;
2025-04-11 12:00:15 -05:00
if ((splitMaskEnv = getenv("NCCL_TESTS_SPLIT_MASK"))) {
2025-01-23 11:09:09 -08:00
color = proc & strtoul(splitMaskEnv, NULL, 16);
2025-04-11 12:00:15 -05:00
} else if ((splitMaskEnv = getenv("NCCL_TESTS_SPLIT"))) {
2025-01-23 11:09:09 -08:00
if (
(strncasecmp(splitMaskEnv, "AND", strlen("AND")) == 0 && parseInt(splitMaskEnv + strlen("AND"), &color)) ||
(strncasecmp(splitMaskEnv, "&", strlen("&")) == 0 && parseInt(splitMaskEnv + strlen("&"), &color))
)
color = proc & color;
if (
(strncasecmp(splitMaskEnv, "OR", strlen("OR")) == 0 && parseInt(splitMaskEnv + strlen("OR"), &color)) ||
(strncasecmp(splitMaskEnv, "|", strlen("|")) == 0 && parseInt(splitMaskEnv + strlen("|"), &color))
)
color = proc | color;
if (
(strncasecmp(splitMaskEnv, "MOD", strlen("MOD")) == 0 && parseInt(splitMaskEnv + strlen("MOD"), &color)) ||
(strncasecmp(splitMaskEnv, "%", strlen("%")) == 0 && parseInt(splitMaskEnv + strlen("%"), &color))
)
color = proc % color;
if (
(strncasecmp(splitMaskEnv, "DIV", strlen("DIV")) == 0 && parseInt(splitMaskEnv + strlen("DIV"), &color)) ||
(strncasecmp(splitMaskEnv, "/", strlen("/")) == 0 && parseInt(splitMaskEnv + strlen("/"), &color))
)
color = proc / color;
}
2022-09-20 02:21:36 -07:00
MPI_Comm mpi_comm;
MPI_Comm_split(MPI_COMM_WORLD, color, proc, &mpi_comm);
MPI_Comm_size(mpi_comm, &ncclProcs);
MPI_Comm_rank(mpi_comm, &ncclProc);
2023-06-14 08:01:50 -07:00
2023-06-14 20:26:33 +00:00
for (int p=0; p<totalProcs; p++) {
2023-04-27 14:06:17 -04:00
if (hostHashs[p] == hostHashs[proc]) localSize++;
}
2024-05-02 11:14:57 -07:00
if (nGpus * localSize > numDevices && numDevices != 1)
2023-04-27 14:06:17 -04:00
{
fprintf(stderr, "[ERROR] The number of requested GPUs (%d) is greater than the number of GPUs available (%d) on node (%s)\n", nGpus*localSize, numDevices, hostname);
return testNcclError;
}
2017-08-08 16:18:34 -07:00
#endif
2022-09-20 02:21:36 -07:00
is_main_thread = is_main_proc = (proc == 0) ? 1 : 0;
2017-08-08 16:18:34 -07:00
2025-06-03 11:43:02 -04:00
PRINT("# Collective test starting: %s\n", program_invocation_short_name);
2022-08-19 15:15:10 -05:00
PRINT("# nThread %d nGpus %d minBytes %ld maxBytes %ld step: %ld(%s) warmup iters: %d iters: %d agg iters: %d validation: %d graph: %d\n",
nThreads, nGpus, minBytes, maxBytes,
(stepFactor > 1)?stepFactor:stepBytes, (stepFactor > 1)?"factor":"bytes",
warmup_iters, iters, agg_iters, datacheck, cudaGraphLaunches);
2019-03-06 18:17:20 -08:00
if (blocking_coll) PRINT("# Blocking Enabled: wait for completion and barrier after each collective \n");
if (parallel_init) PRINT("# Parallel Init Enabled: threads call into NcclInitRank concurrently \n");
PRINT("#\n");
2024-03-28 14:03:59 -05:00
PRINT("rccl-tests: Version %s\n", rcclTestsGitHash);
2019-03-06 18:17:20 -08:00
PRINT("# Using devices\n");
#define MAX_LINE 2048
char line[MAX_LINE];
int len = 0;
2021-06-28 18:23:12 -07:00
size_t maxMem = ~0;
2022-09-20 02:21:36 -07:00
char* envstr = getenv("NCCL_TESTS_DEVICE");
int gpu0 = envstr ? atoi(envstr) : -1;
2019-03-06 18:17:20 -08:00
for (int i=0; i<nThreads*nGpus; i++) {
2024-05-02 11:14:57 -07:00
int cudaDev = ((gpu0 != -1 ? gpu0 : localRank*nThreads*nGpus) + i)%numDevices;
2019-03-06 18:17:20 -08:00
int rank = proc*nThreads*nGpus+i;
2024-03-05 09:47:18 -07:00
cudaDeviceProp prop;
CUDACHECK(cudaGetDeviceProperties(&prop, cudaDev));
2025-04-11 12:00:15 -05:00
//char busIdStr[] = "00000000:00:00.0";
//CUDACHECK(cudaDeviceGetPCIBusId(busIdStr, sizeof(busIdStr), cudaDev));
//len += snprintf(line+len, MAX_LINE>len ? MAX_LINE-len : 0, "# Rank %2d Group %2d Pid %6d on %10s device %2d [%04x:%s:%02x] %s\n",
// rank, color, getpid(), hostname, cudaDev, prop.pciDomainID, busIdStr, prop.pciDeviceID, prop.name);
len += snprintf(line+len, MAX_LINE>len ? MAX_LINE-len : 0, "# Rank %2d Group %2d Pid %6d on %10s device %2d [%04x:%02x:%02x] %s\n",
2025-02-28 13:23:26 -08:00
rank, color, getpid(), hostname, cudaDev, prop.pciDomainID, prop.pciBusID, prop.pciDeviceID, prop.name);
2021-06-28 18:23:12 -07:00
maxMem = std::min(maxMem, prop.totalGlobalMem);
2022-03-18 11:42:15 -04:00
}
2019-03-06 18:17:20 -08:00
#if MPI_SUPPORT
2022-09-20 02:21:36 -07:00
char *lines = (proc == 0) ? (char *)malloc(totalProcs*MAX_LINE) : NULL;
2019-03-06 18:17:20 -08:00
// Gather all output in rank order to root (0)
MPI_Gather(line, MAX_LINE, MPI_BYTE, lines, MAX_LINE, MPI_BYTE, 0, MPI_COMM_WORLD);
if (proc == 0) {
2022-09-20 02:21:36 -07:00
for (int p = 0; p < totalProcs; p++)
2019-03-06 18:17:20 -08:00
PRINT("%s", lines+MAX_LINE*p);
free(lines);
2017-08-08 16:18:34 -07:00
}
2021-06-28 18:23:12 -07:00
MPI_Allreduce(MPI_IN_PLACE, &maxMem, 1, MPI_LONG, MPI_MIN, MPI_COMM_WORLD);
2019-03-06 18:17:20 -08:00
#else
PRINT("%s", line);
#endif
2017-08-08 16:18:34 -07:00
2025-05-21 09:40:26 -07:00
// Reserve 1GiB of memory for each 16GiB installed, but limit to a max of 4GiB
const size_t GB = (1ULL << 30);
size_t reserveMem = std::min(DIVUP(maxMem, 16*GB) * 1*GB, 4*GB);
2021-06-28 18:23:12 -07:00
// We need sendbuff, recvbuff, expected (when datacheck enabled), plus 1G for the rest.
2025-05-21 09:40:26 -07:00
size_t memMaxBytes = (maxMem - reserveMem - 1*GB) / (datacheck ? 3 : 2);
2021-06-28 18:23:12 -07:00
if (maxBytes > memMaxBytes) {
maxBytes = memMaxBytes;
2025-05-21 09:40:26 -07:00
if (minBytes > maxBytes) minBytes = maxBytes;
2021-06-28 18:23:12 -07:00
if (proc == 0) printf("#\n# Reducing maxBytes to %ld due to memory limitation\n", maxBytes);
}
2017-08-08 16:18:34 -07:00
ncclUniqueId ncclId;
2022-09-20 02:21:36 -07:00
if (ncclProc == 0) {
2017-08-08 16:18:34 -07:00
NCCLCHECK(ncclGetUniqueId(&ncclId));
}
#ifdef MPI_SUPPORT
2022-09-20 02:21:36 -07:00
MPI_Bcast(&ncclId, sizeof(ncclId), MPI_BYTE, 0, mpi_comm);
2023-10-12 16:53:32 -07:00
MPI_Barrier(MPI_COMM_WORLD); // Ensure Bcast is complete for HCOLL
2017-08-08 16:18:34 -07:00
#endif
2022-10-17 14:13:48 +00:00
2025-04-11 12:00:15 -05:00
std::vector<int> gpus(nGpus*nThreads);
std::vector<cudaStream_t> streams(nGpus*nThreads);
std::vector<void*> sendbuffs(nGpus*nThreads);
std::vector<void*> recvbuffs(nGpus*nThreads);
std::vector<void*> expected(nGpus*nThreads);
2019-03-06 18:17:20 -08:00
size_t sendBytes, recvBytes;
2017-08-08 16:18:34 -07:00
2022-09-20 02:21:36 -07:00
ncclTestEngine.getBuffSize(&sendBytes, &recvBytes, (size_t)maxBytes, (size_t)ncclProcs*nGpus*nThreads);
2022-03-18 11:42:15 -04:00
2022-09-20 02:21:36 -07:00
envstr = getenv("NCCL_TESTS_DEVICE");
gpu0 = envstr ? atoi(envstr) : -1;
2017-08-08 16:18:34 -07:00
for (int i=0; i<nGpus*nThreads; i++) {
2024-05-02 11:14:57 -07:00
gpus[i] = ((gpu0 != -1 ? gpu0 : localRank*nThreads*nGpus) + i)%numDevices;
2024-03-05 09:47:18 -07:00
CUDACHECK(cudaSetDevice(gpus[i]));
2025-04-11 12:00:15 -05:00
TESTCHECK(AllocateBuffs(sendbuffs.data()+i, sendBytes, recvbuffs.data()+i, recvBytes, expected.data()+i, (size_t)maxBytes));
2025-05-14 15:30:07 -05:00
if (streamnull) {
2022-09-20 02:21:36 -07:00
streams[i] = NULL;
2025-05-14 15:30:07 -05:00
}
else {
2025-04-11 12:00:15 -05:00
CUDACHECK(cudaStreamCreateWithFlags(streams.data()+i, cudaStreamNonBlocking));
2025-05-14 15:30:07 -05:00
}
int archMajor, archMinor;
CUDACHECK(cudaDeviceGetAttribute(&archMajor, cudaDevAttrComputeCapabilityMajor, gpus[i]));
CUDACHECK(cudaDeviceGetAttribute(&archMinor, cudaDevAttrComputeCapabilityMinor, gpus[i]));
minCudaArch = std::min(minCudaArch, 100*archMajor + 10*archMinor);
2017-08-08 16:18:34 -07:00
}
2025-05-14 15:30:07 -05:00
#ifdef MPI_SUPPORT
MPI_Allreduce(MPI_IN_PLACE, &minCudaArch, 1, MPI_INT, MPI_MIN, MPI_COMM_WORLD);
#endif
2025-07-23 14:22:18 -05:00
#if defined(RCCL_FLOAT8)
2025-04-18 19:20:59 -07:00
if (NCCL_VERSION_CODE >= NCCL_VERSION(2,24,0) && test_ncclVersion >= NCCL_VERSION(2,24,0)) {
if (minCudaArch < 900) { // Filter out fp8 on pre-Hopper hardware
int n = 0;
for (int i=0; i < test_typenum; i++) {
if (!(test_types[i] == ncclFloat8e4m3 || test_types[i] == ncclFloat8e5m2)) {
test_types[n] = test_types[i];
test_typenames[n] = test_typenames[i];
n += 1;
}
}
test_typenum = n;
}
2017-08-08 16:18:34 -07:00
}
2025-04-18 19:20:59 -07:00
#endif
2017-08-08 16:18:34 -07:00
//if parallel init is not selected, use main thread to initialize NCCL
ncclComm_t* comms = (ncclComm_t*)malloc(sizeof(ncclComm_t)*nThreads*nGpus);
2024-02-28 05:18:40 -08:00
#if NCCL_VERSION_CODE >= NCCL_VERSION(2,19,0)
void **sendRegHandles = NULL;
void **recvRegHandles = NULL;
#endif
2017-08-08 16:18:34 -07:00
if (!parallel_init) {
2022-09-20 02:21:36 -07:00
if (ncclProcs == 1) {
2025-04-11 12:00:15 -05:00
NCCLCHECK(ncclCommInitAll(comms, nGpus*nThreads, gpus.data()));
2017-08-08 16:18:34 -07:00
} else {
NCCLCHECK(ncclGroupStart());
for (int i=0; i<nGpus*nThreads; i++) {
2024-03-05 09:47:18 -07:00
CUDACHECK(cudaSetDevice(gpus[i]));
2022-09-20 02:21:36 -07:00
NCCLCHECK(ncclCommInitRank(comms+i, ncclProcs*nThreads*nGpus, ncclId, ncclProc*nThreads*nGpus+i));
2017-08-08 16:18:34 -07:00
}
NCCLCHECK(ncclGroupEnd());
}
2024-02-28 05:18:40 -08:00
#if NCCL_VERSION_CODE >= NCCL_VERSION(2,19,0)
2025-06-03 10:36:53 -07:00
NCCLCHECK(ncclGroupStart());
2024-02-28 05:18:40 -08:00
sendRegHandles = (local_register) ? (void **)malloc(sizeof(*sendRegHandles)*nThreads*nGpus) : NULL;
recvRegHandles = (local_register) ? (void **)malloc(sizeof(*recvRegHandles)*nThreads*nGpus) : NULL;
for (int i=0; i<nGpus*nThreads; i++) {
2025-05-19 18:20:22 -07:00
#if NCCL_VERSION_CODE >= NCCL_VERSION(2,27,0)
if (test_ncclVersion >= NCCL_VERSION(2,27,0) && (local_register == SYMMETRIC_REGISTER)) {
NCCLCHECK(ncclCommWindowRegister(comms[i], sendbuffs[i], maxBytes, (ncclWindow_t*)&sendRegHandles[i], NCCL_WIN_COLL_SYMMETRIC));
NCCLCHECK(ncclCommWindowRegister(comms[i], recvbuffs[i], maxBytes, (ncclWindow_t*)&recvRegHandles[i], NCCL_WIN_COLL_SYMMETRIC));
} else
#endif
{
if (local_register) NCCLCHECK(ncclCommRegister(comms[i], sendbuffs[i], maxBytes, &sendRegHandles[i]));
if (local_register) NCCLCHECK(ncclCommRegister(comms[i], recvbuffs[i], maxBytes, &recvRegHandles[i]));
}
2024-02-28 05:18:40 -08:00
}
2025-06-03 10:36:53 -07:00
NCCLCHECK(ncclGroupEnd());
2024-02-28 05:18:40 -08:00
#endif
2017-08-08 16:18:34 -07:00
}
2025-04-11 12:00:15 -05:00
std::vector<int> errors(nThreads);
std::vector<double> bw(nThreads);
2019-03-06 18:17:20 -08:00
double* delta;
2024-03-05 09:47:18 -07:00
CUDACHECK(hipHostMalloc(&delta, sizeof(double)*nThreads*NUM_BLOCKS, cudaHostAllocPortable | cudaHostAllocMapped));
2025-04-11 12:00:15 -05:00
std::vector<int> bw_count(nThreads);
2017-08-08 16:18:34 -07:00
for (int t=0; t<nThreads; t++) {
bw[t] = 0.0;
errors[t] = bw_count[t] = 0;
}
2022-11-22 11:16:47 -08:00
fflush(stdout);
2022-09-20 02:21:36 -07:00
const char* timeStr = report_cputime ? "cputime" : "time";
2019-03-06 18:17:20 -08:00
PRINT("#\n");
2025-04-03 17:31:54 -05:00
if (enable_out_of_place && enable_in_place) {
2024-01-04 16:20:42 -06:00
PRINT("# %10s %12s %8s %6s %6s out-of-place in-place \n", "", "", "", "", "");
PRINT("# %10s %12s %8s %6s %6s %7s %6s %6s %6s %7s %6s %6s %6s\n", "size", "count", "type", "redop", "root",
timeStr, "algbw", "busbw", "#wrong", timeStr, "algbw", "busbw", "#wrong");
PRINT("# %10s %12s %8s %6s %6s %7s %6s %6s %5s %7s %6s %6s %5s\n", "(B)", "(elements)", "", "", "",
"(us)", "(GB/s)", "(GB/s)", "", "(us)", "(GB/s)", "(GB/s)", "");
2025-04-03 17:31:54 -05:00
} else if (enable_out_of_place) {
PRINT("# %10s %12s %8s %6s %6s out-of-place \n", "", "", "", "", "");
PRINT("# %10s %12s %8s %6s %6s %7s %6s %6s %6s\n", "size", "count", "type", "redop", "root",
timeStr, "algbw", "busbw", "#wrong");
PRINT("# %10s %12s %8s %6s %6s %7s %6s %6s %5s\n", "(B)", "(elements)", "", "", "",
"(us)", "(GB/s)", "(GB/s)", "");
2024-01-04 16:20:42 -06:00
} else {
2025-04-03 17:31:54 -05:00
PRINT("# %10s %12s %8s %6s %6s in-place \n", "", "", "", "", "");
2024-01-04 16:20:42 -06:00
PRINT("# %10s %12s %8s %6s %6s %7s %6s %6s %6s\n", "size", "count", "type", "redop", "root",
timeStr, "algbw", "busbw", "#wrong");
PRINT("# %10s %12s %8s %6s %6s %7s %6s %6s %5s\n", "(B)", "(elements)", "", "", "",
"(us)", "(GB/s)", "(GB/s)", "");
}
2025-01-13 15:28:29 -08:00
Reporter reporter(output_file, output_format);
2017-08-08 16:18:34 -07:00
2025-04-11 12:00:15 -05:00
std::vector<testThread> threads(nThreads);
memset(threads.data(), 0, sizeof(struct testThread)*nThreads);
2017-08-08 16:18:34 -07:00
for (int t=nThreads-1; t>=0; t--) {
2019-03-06 18:17:20 -08:00
threads[t].args.minbytes=minBytes;
threads[t].args.maxbytes=maxBytes;
threads[t].args.stepbytes=stepBytes;
threads[t].args.stepfactor=stepFactor;
threads[t].args.localRank = localRank;
2022-10-17 14:13:48 +00:00
threads[t].args.totalProcs = totalProcs;
2022-09-20 02:21:36 -07:00
threads[t].args.nProcs=ncclProcs;
threads[t].args.proc=ncclProc;
2019-03-06 18:17:20 -08:00
threads[t].args.nThreads=nThreads;
threads[t].args.thread=t;
threads[t].args.nGpus=nGpus;
2025-04-11 12:00:15 -05:00
threads[t].args.gpus=gpus.data()+t*nGpus;
threads[t].args.sendbuffs = sendbuffs.data()+t*nGpus;
threads[t].args.recvbuffs = recvbuffs.data()+t*nGpus;
threads[t].args.expected = expected.data()+t*nGpus;
2019-03-06 18:17:20 -08:00
threads[t].args.ncclId = ncclId;
threads[t].args.comms=comms+t*nGpus;
2025-04-11 12:00:15 -05:00
threads[t].args.streams=streams.data()+t*nGpus;
2024-01-04 16:20:42 -06:00
threads[t].args.enable_out_of_place=enable_out_of_place;
2025-04-03 17:31:54 -05:00
threads[t].args.enable_in_place=enable_in_place;
2024-05-07 11:09:32 -05:00
threads[t].args.enable_cache_flush = enable_cache_flush;
2024-06-04 11:35:39 -05:00
threads[t].args.enable_rotating_tensor = enable_rotating_tensor;
2025-04-11 12:00:15 -05:00
threads[t].args.errors=errors.data()+t;
threads[t].args.bw=bw.data()+t;
threads[t].args.bw_count=bw_count.data()+t;
2019-03-06 18:17:20 -08:00
2022-09-07 11:23:49 -07:00
threads[t].args.reportErrors = datacheck;
2025-01-13 15:28:29 -08:00
threads[t].args.reporter = &reporter;
2020-03-17 12:00:19 -07:00
2019-03-06 18:17:20 -08:00
threads[t].func = parallel_init ? threadInit : threadRunTests;
if (t)
2025-04-11 12:00:15 -05:00
TESTCHECK(threadLaunch(threads.data()+t));
2019-03-06 18:17:20 -08:00
else
TESTCHECK(threads[t].func(&threads[t].args));
}
// Wait for other threads and accumulate stats and errors
2017-08-08 16:18:34 -07:00
for (int t=nThreads-1; t>=0; t--) {
2019-03-06 18:17:20 -08:00
if (t) pthread_join(threads[t].thread, NULL);
TESTCHECK(threads[t].ret);
if (t) {
errors[0] += errors[t];
bw[0] += bw[t];
bw_count[0] += bw_count[t];
}
2017-08-08 16:18:34 -07:00
}
#ifdef MPI_SUPPORT
2019-03-06 18:17:20 -08:00
MPI_Allreduce(MPI_IN_PLACE, &errors[0], 1, MPI_INT, MPI_SUM, MPI_COMM_WORLD);
2017-08-08 16:18:34 -07:00
#endif
2019-03-06 18:17:20 -08:00
if (!parallel_init) {
2024-02-28 05:18:40 -08:00
for(int i=0; i<nGpus*nThreads; ++i) {
#if NCCL_VERSION_CODE >= NCCL_VERSION(2,19,0)
2025-05-19 18:20:22 -07:00
#if NCCL_VERSION_CODE >= NCCL_VERSION(2,27,0)
if (test_ncclVersion >= NCCL_VERSION(2,27,0) && (local_register == SYMMETRIC_REGISTER)) {
NCCLCHECK(ncclCommWindowDeregister(comms[i], (ncclWindow_t)sendRegHandles[i]));
NCCLCHECK(ncclCommWindowDeregister(comms[i], (ncclWindow_t)recvRegHandles[i]));
} else
#endif
{
if (local_register) NCCLCHECK(ncclCommDeregister(comms[i], sendRegHandles[i]));
if (local_register) NCCLCHECK(ncclCommDeregister(comms[i], recvRegHandles[i]));
}
2024-02-28 05:18:40 -08:00
#endif
2019-03-06 18:17:20 -08:00
NCCLCHECK(ncclCommDestroy(comms[i]));
2024-02-28 05:18:40 -08:00
}
2019-03-06 18:17:20 -08:00
free(comms);
}
// Free off CUDA allocated memory
for (int i=0; i<nGpus*nThreads; i++) {
2025-07-24 11:14:49 -04:00
#if NCCL_VERSION_CODE >= NCCL_VERSION(2,19,0)
if (sendbuffs[i]) NCCLCHECK(ncclMemFree((char*)sendbuffs[i]));
if (recvbuffs[i]) NCCLCHECK(ncclMemFree((char*)recvbuffs[i]));
if (datacheck) NCCLCHECK(ncclMemFree(expected[i]));
#else
2024-03-05 09:47:18 -07:00
if (sendbuffs[i]) CUDACHECK(cudaFree((char*)sendbuffs[i]));
if (recvbuffs[i]) CUDACHECK(cudaFree((char*)recvbuffs[i]));
if (datacheck) CUDACHECK(cudaFree(expected[i]));
2025-07-24 11:14:49 -04:00
#endif
2019-03-06 18:17:20 -08:00
}
2024-03-05 09:47:18 -07:00
CUDACHECK(cudaFreeHost(delta));
2024-02-28 05:18:40 -08:00
#if NCCL_VERSION_CODE >= NCCL_VERSION(2,19,0)
free(sendRegHandles);
free(recvRegHandles);
#endif
2017-08-08 16:18:34 -07:00
2022-09-20 02:21:36 -07:00
envstr = getenv("NCCL_TESTS_MIN_BW");
double check_avg_bw = envstr ? atof(envstr) : -1;
2017-08-08 16:18:34 -07:00
bw[0] /= bw_count[0];
2019-06-28 09:52:44 -06:00
if (datacheck) PRINT("# Errors with asterisks indicate errors that have exceeded the maximum threshold.\n");
2019-03-06 18:17:20 -08:00
PRINT("# Out of bounds values : %d %s\n", errors[0], errors[0] ? "FAILED" : "OK");
PRINT("# Avg bus bandwidth : %g %s\n", bw[0], check_avg_bw == -1 ? "" : (bw[0] < check_avg_bw*(0.9) ? "FAILED" : "OK"));
PRINT("#\n");
2025-06-03 11:43:02 -04:00
PRINT("# Collective test concluded: %s\n", program_invocation_short_name);
2017-08-08 16:18:34 -07:00
#ifdef MPI_SUPPORT
2024-02-05 08:53:54 -08:00
MPI_Comm_free(&mpi_comm);
2017-08-08 16:18:34 -07:00
MPI_Finalize();
#endif
2019-03-06 18:17:20 -08:00
2025-07-30 17:28:04 -05:00
reporter.writeFile();
2024-03-05 09:47:18 -07:00
// 'cuda-memcheck --leak-check full' requires this
2022-09-20 02:21:36 -07:00
PRINT("%s\n", ncclGetLastError(NULL));
2024-03-05 09:47:18 -07:00
cudaDeviceReset();
2019-03-06 18:17:20 -08:00
2017-08-08 16:18:34 -07:00
if (errors[0] || bw[0] < check_avg_bw*(0.9))
exit(EXIT_FAILURE);
2019-03-06 18:17:20 -08:00
else
2017-08-08 16:18:34 -07:00
exit(EXIT_SUCCESS);
}