2.3.5-5
Add support for inter-node communication using sockets and InfiniBand/RoCE.
Improve latency.
Add support for aggregation.
Improve LL/regular tuning.
Remove tests as those are now at github.com/nvidia/nccl-tests .
[ROCm/rccl commit: f93fe9bfd9]
This commit is contained in:
@@ -1,438 +0,0 @@
|
||||
/*************************************************************************
|
||||
* Copyright (c) 2015-2016, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* See LICENCE.txt for license information
|
||||
************************************************************************/
|
||||
|
||||
|
||||
#ifndef SRC_TEST_UTILITIES_H_
|
||||
#define SRC_TEST_UTILITIES_H_
|
||||
|
||||
#include <curand.h>
|
||||
#include <cerrno>
|
||||
#include <string>
|
||||
|
||||
#define CUDACHECK(cmd) do { \
|
||||
cudaError_t e = cmd; \
|
||||
if( e != cudaSuccess ) { \
|
||||
printf("Cuda failure %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("NCCL failure %s:%d '%s'\n", \
|
||||
__FILE__,__LINE__,ncclGetErrorString(r)); \
|
||||
exit(EXIT_FAILURE); \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
template<typename T>
|
||||
void Randomize(T* const dest, const int N, const int randomSeed);
|
||||
|
||||
template<typename T>
|
||||
void Accumulate(T* dest, const T* contrib, int N, ncclRedOp_t op);
|
||||
|
||||
template<typename T>
|
||||
double CheckDelta(const T* results, const T* expected, int N);
|
||||
|
||||
#define CURAND_CHK(cmd) \
|
||||
do { \
|
||||
curandStatus_t error = (cmd); \
|
||||
if (error != CURAND_STATUS_SUCCESS) { \
|
||||
printf("CuRAND error %i at %s:%i\n", error, __FILE__ , __LINE__); \
|
||||
exit(EXIT_FAILURE); \
|
||||
} \
|
||||
} while (false)
|
||||
|
||||
|
||||
template<typename T>
|
||||
void GenerateRandom(curandGenerator_t generator, T * const dest,
|
||||
const int N);
|
||||
|
||||
template<>
|
||||
void GenerateRandom<char>(curandGenerator_t generator, char * const dest,
|
||||
const int N) {
|
||||
CURAND_CHK(curandGenerate(generator, (unsigned int*)dest,
|
||||
N * sizeof(char) / sizeof(int)));
|
||||
}
|
||||
|
||||
template<>
|
||||
void GenerateRandom<int>(curandGenerator_t generator, int * const dest,
|
||||
const int N) {
|
||||
CURAND_CHK(curandGenerate(generator, (unsigned int*)dest, N));
|
||||
}
|
||||
|
||||
template<>
|
||||
void GenerateRandom<float>(curandGenerator_t generator, float * const dest,
|
||||
const int N) {
|
||||
CURAND_CHK(curandGenerateUniform(generator, dest, N));
|
||||
}
|
||||
|
||||
template<>
|
||||
void GenerateRandom<double>(curandGenerator_t generator, double * const dest,
|
||||
const int N) {
|
||||
CURAND_CHK(curandGenerateUniformDouble(generator, dest, N));
|
||||
}
|
||||
|
||||
template<>
|
||||
void GenerateRandom<unsigned long long>(curandGenerator_t generator, unsigned long long * const dest,
|
||||
const int N) {
|
||||
CURAND_CHK(curandGenerateLongLong(generator, dest, N));
|
||||
}
|
||||
|
||||
|
||||
template<typename T>
|
||||
void Randomize(T* const dest, const int N, const int randomSeed) {
|
||||
curandGenerator_t gen;
|
||||
CURAND_CHK(curandCreateGenerator(&gen, CURAND_RNG_PSEUDO_MTGP32));
|
||||
CURAND_CHK(curandSetPseudoRandomGeneratorSeed(gen, randomSeed));
|
||||
GenerateRandom<T>(gen, dest, N);
|
||||
CURAND_CHK(curandDestroyGenerator(gen));
|
||||
CUDACHECK(cudaDeviceSynchronize());
|
||||
}
|
||||
|
||||
template<>
|
||||
void Randomize(unsigned long long* const dest, const int N, const int randomSeed) {
|
||||
curandGenerator_t gen;
|
||||
CURAND_CHK(curandCreateGenerator(&gen, CURAND_RNG_QUASI_SOBOL64));
|
||||
GenerateRandom<unsigned long long>(gen, dest, N);
|
||||
CURAND_CHK(curandDestroyGenerator(gen));
|
||||
CUDACHECK(cudaDeviceSynchronize());
|
||||
}
|
||||
|
||||
template<>
|
||||
void Randomize(long long* const dest, const int N, const int randomSeed) {
|
||||
curandGenerator_t gen;
|
||||
CURAND_CHK(curandCreateGenerator(&gen, CURAND_RNG_QUASI_SOBOL64));
|
||||
GenerateRandom<unsigned long long>(gen, (unsigned long long *)dest, N);
|
||||
CURAND_CHK(curandDestroyGenerator(gen));
|
||||
CUDACHECK(cudaDeviceSynchronize());
|
||||
}
|
||||
|
||||
#ifdef CUDA_HAS_HALF
|
||||
__global__ void halve(const float * src, half* dest, int N) {
|
||||
for(int tid = threadIdx.x + blockIdx.x*blockDim.x;
|
||||
tid < N; tid += blockDim.x * gridDim.x)
|
||||
dest[tid] = __float2half(src[tid]);
|
||||
}
|
||||
|
||||
template<>
|
||||
void Randomize<half>(half* const dest, const int N, const int randomSeed) {
|
||||
curandGenerator_t gen;
|
||||
CURAND_CHK(curandCreateGenerator(&gen, CURAND_RNG_PSEUDO_MTGP32));
|
||||
CURAND_CHK(curandSetPseudoRandomGeneratorSeed(gen, randomSeed));
|
||||
|
||||
float* temp;
|
||||
CUDACHECK(cudaMalloc(&temp, N*sizeof(float)));
|
||||
GenerateRandom<float>(gen, temp, N);
|
||||
halve<<<128, 512>>>(temp, dest, N);
|
||||
CURAND_CHK(curandDestroyGenerator(gen));
|
||||
CUDACHECK(cudaFree(temp));
|
||||
CUDACHECK(cudaDeviceSynchronize());
|
||||
}
|
||||
#endif
|
||||
|
||||
void makeRandom(void* ptr, int n, ncclDataType_t type, int seed) {
|
||||
if (type == ncclChar)
|
||||
Randomize<char>((char*)ptr, n, seed);
|
||||
else if (type == ncclInt)
|
||||
Randomize<int>((int*)ptr, n, seed);
|
||||
#ifdef CUDA_HAS_HALF
|
||||
else if (type == ncclHalf)
|
||||
Randomize<half>((half*)ptr, n, seed);
|
||||
#endif
|
||||
else if (type == ncclFloat)
|
||||
Randomize<float>((float*)ptr, n, seed);
|
||||
else if (type == ncclDouble)
|
||||
Randomize<double>((double*)ptr, n, seed);
|
||||
else if (type == ncclInt64)
|
||||
Randomize<long long>((long long*)ptr, n, seed);
|
||||
else if (type == ncclUint64)
|
||||
Randomize<unsigned long long>((unsigned long long*)ptr, n, seed);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
template<typename T, int OP> __global__ static
|
||||
void accumKern(T* acum, const T* contrib, int N) {
|
||||
int tid = threadIdx.x + blockIdx.x*blockDim.x;
|
||||
int offset = blockDim.x*gridDim.x;
|
||||
for(int i=tid; i<N; i+=offset) {
|
||||
T c = contrib[i];
|
||||
T a = acum[i];
|
||||
if(OP == ncclSum) {
|
||||
acum[i] = a+c;
|
||||
} else if(OP == ncclProd) {
|
||||
acum[i] = a*c;
|
||||
} else if(OP == ncclMax) {
|
||||
acum[i] = (a > c) ? a : c;
|
||||
} else if(OP == ncclMin) {
|
||||
acum[i] = (a < c) ? a : c;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef CUDA_HAS_HALF
|
||||
template<> __global__
|
||||
void accumKern<half, ncclSum>(half* acum, const half* contrib, int N) {
|
||||
int tid = threadIdx.x + blockIdx.x*blockDim.x;
|
||||
int offset = blockDim.x*gridDim.x;
|
||||
for(int i=tid; i<N; i+=offset) {
|
||||
float c = __half2float(contrib[i]);
|
||||
float a = __half2float(acum[i]);
|
||||
acum[i] = __float2half( a + c );
|
||||
}
|
||||
}
|
||||
|
||||
template<> __global__
|
||||
void accumKern<half, ncclProd>(half* acum, const half* contrib, int N) {
|
||||
int tid = threadIdx.x + blockIdx.x*blockDim.x;
|
||||
int offset = blockDim.x*gridDim.x;
|
||||
for(int i=tid; i<N; i+=offset) {
|
||||
float c = __half2float(contrib[i]);
|
||||
float a = __half2float(acum[i]);
|
||||
acum[i] = __float2half( a * c );
|
||||
}
|
||||
}
|
||||
|
||||
template<> __global__
|
||||
void accumKern<half, ncclMax>(half* acum, const half* contrib, int N) {
|
||||
int tid = threadIdx.x + blockIdx.x*blockDim.x;
|
||||
int offset = blockDim.x*gridDim.x;
|
||||
for(int i=tid; i<N; i+=offset) {
|
||||
float c = __half2float(contrib[i]);
|
||||
float a = __half2float(acum[i]);
|
||||
acum[i] = __float2half( (a>c) ? a : c );
|
||||
}
|
||||
}
|
||||
|
||||
template<> __global__
|
||||
void accumKern<half, ncclMin>(half* acum, const half* contrib, int N) {
|
||||
int tid = threadIdx.x + blockIdx.x*blockDim.x;
|
||||
int offset = blockDim.x*gridDim.x;
|
||||
for(int i=tid; i<N; i+=offset) {
|
||||
float c = __half2float(contrib[i]);
|
||||
float a = __half2float(acum[i]);
|
||||
acum[i] = __float2half( (a<c) ? a : c );
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
template<typename T>
|
||||
void accVecType(void* out, void* in, int n, ncclRedOp_t op) {
|
||||
switch(op) {
|
||||
case ncclSum: accumKern<T, ncclSum> <<<256,256>>>((T*)out, (T*)in, n); break;
|
||||
case ncclProd: accumKern<T, ncclProd><<<256,256>>>((T*)out, (T*)in, n); break;
|
||||
case ncclMax: accumKern<T, ncclMax> <<<256,256>>>((T*)out, (T*)in, n); break;
|
||||
case ncclMin: accumKern<T, ncclMin> <<<256,256>>>((T*)out, (T*)in, n); break;
|
||||
default:
|
||||
printf("Unknown reduction operation.\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void Accumulate(T* dest, const T* contrib, int N, ncclRedOp_t op) {
|
||||
|
||||
T* devdest;
|
||||
CUDACHECK(cudaHostRegister(dest, N*sizeof(T), 0));
|
||||
CUDACHECK(cudaHostGetDevicePointer(&devdest, dest, 0));
|
||||
accVecType<T>((void*)devdest, (void*)contrib, N, op);
|
||||
CUDACHECK(cudaHostUnregister(dest));
|
||||
}
|
||||
|
||||
void accVec(void* out, void* in, int n, ncclDataType_t type, ncclRedOp_t op) {
|
||||
switch (type) {
|
||||
case ncclChar: accVecType<char> (out, in, n, op); break;
|
||||
case ncclInt: accVecType<int> (out, in, n, op); break;
|
||||
#ifdef CUDA_HAS_HALF
|
||||
case ncclHalf: accVecType<half> (out, in, n, op); break;
|
||||
#endif
|
||||
case ncclFloat: accVecType<float> (out, in, n, op); break;
|
||||
case ncclDouble: accVecType<double> (out, in, n, op); break;
|
||||
case ncclInt64: accVecType<long long> (out, in, n, op); break;
|
||||
case ncclUint64: accVecType<unsigned long long> (out, in, n, op); break;
|
||||
default:
|
||||
printf("Unknown reduction type.\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T> __device__
|
||||
double absDiff(T a, T b) {
|
||||
return fabs((double)(b - a));
|
||||
}
|
||||
|
||||
#ifdef CUDA_HAS_HALF
|
||||
template<> __device__
|
||||
double absDiff<half>(half a, half b) {
|
||||
float x = __half2float(a);
|
||||
float y = __half2float(b);
|
||||
return fabs((double)(y-x));
|
||||
}
|
||||
#endif
|
||||
|
||||
template<typename T, int BSIZE> __global__
|
||||
void deltaKern(const T* A, const T* B, int N, double* max) {
|
||||
__shared__ double temp[BSIZE];
|
||||
int tid = threadIdx.x;
|
||||
double locmax = 0.0;
|
||||
for(int i=tid; i<N; i+=blockDim.x) {
|
||||
|
||||
double delta = absDiff(A[i], B[i]);
|
||||
if( delta > locmax )
|
||||
locmax = delta;
|
||||
}
|
||||
|
||||
temp[tid] = locmax;
|
||||
for(int stride = BSIZE/2; stride > 1; stride>>=1) {
|
||||
__syncthreads();
|
||||
if( tid < stride )
|
||||
temp[tid] = temp[tid] > temp[tid+stride] ? temp[tid] : temp[tid+stride];
|
||||
}
|
||||
__syncthreads();
|
||||
if( threadIdx.x == 0)
|
||||
*max = temp[0] > temp[1] ? temp[0] : temp[1];
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
double CheckDelta(const T* results, const T* expected, int N) {
|
||||
T* devexp;
|
||||
double maxerr;
|
||||
double* devmax;
|
||||
CUDACHECK(cudaHostRegister((void*)expected, N*sizeof(T), 0));
|
||||
CUDACHECK(cudaHostGetDevicePointer((void**)&devexp, (void*)expected, 0));
|
||||
CUDACHECK(cudaHostRegister((void*)&maxerr, sizeof(double), 0));
|
||||
CUDACHECK(cudaHostGetDevicePointer(&devmax, &maxerr, 0));
|
||||
deltaKern<T, 512><<<1, 512>>>(results, devexp, N, devmax);
|
||||
CUDACHECK(cudaHostUnregister(&maxerr));
|
||||
CUDACHECK(cudaHostUnregister((void*)expected));
|
||||
return maxerr;
|
||||
}
|
||||
|
||||
void maxDiff(double* max, void* first, void* second, int n, ncclDataType_t type, cudaStream_t s) {
|
||||
switch (type) {
|
||||
case ncclChar: deltaKern<char, 512> <<<1,512,0,s>>>((char*)first, (char*)second, n, max); break;
|
||||
case ncclInt: deltaKern<int, 512> <<<1,512,0,s>>>((int*)first, (int*)second, n, max); break;
|
||||
#ifdef CUDA_HAS_HALF
|
||||
case ncclHalf: deltaKern<half, 512> <<<1,512,0,s>>>((half*)first, (half*)second, n, max); break;
|
||||
#endif
|
||||
case ncclFloat: deltaKern<float, 512> <<<1,512,0,s>>>((float*)first, (float*)second, n, max); break;
|
||||
case ncclDouble: deltaKern<double, 512> <<<1,512,0,s>>>((double*)first, (double*)second, n, max); break;
|
||||
case ncclInt64: deltaKern<long long, 512> <<<1,512,0,s>>>((long long*)first, (long long*)second, n, max); break;
|
||||
case ncclUint64: deltaKern<unsigned long long, 512><<<1,512,0,s>>>((unsigned long long*)first, (unsigned long long*)second, n, max); break;
|
||||
default:
|
||||
printf("Unknown reduction type.\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
std::string TypeName(const ncclDataType_t type) {
|
||||
switch (type) {
|
||||
case ncclChar: return "char";
|
||||
case ncclInt: return "int";
|
||||
#ifdef CUDA_HAS_HALF
|
||||
case ncclHalf: return "half";
|
||||
#endif
|
||||
case ncclFloat: return "float";
|
||||
case ncclDouble: return "double";
|
||||
case ncclInt64: return "int64";
|
||||
case ncclUint64: return "uint64";
|
||||
default: return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
std::string OperationName(const ncclRedOp_t op) {
|
||||
switch (op) {
|
||||
case ncclSum: return "sum";
|
||||
case ncclProd: return "prod";
|
||||
case ncclMax: return "max";
|
||||
case ncclMin: return "min";
|
||||
default: return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
ncclDataType_t strToType(const char* s) {
|
||||
if (strcmp(s, "char") == 0)
|
||||
return ncclChar;
|
||||
if (strcmp(s, "int") == 0)
|
||||
return ncclInt;
|
||||
#ifdef CUDA_HAS_HALF
|
||||
if (strcmp(s, "half") == 0)
|
||||
return ncclHalf;
|
||||
#endif
|
||||
if (strcmp(s, "float") == 0)
|
||||
return ncclFloat;
|
||||
if (strcmp(s, "double") == 0)
|
||||
return ncclDouble;
|
||||
if (strcmp(s, "int64") == 0)
|
||||
return ncclInt64;
|
||||
if (strcmp(s, "uint64") == 0)
|
||||
return ncclUint64;
|
||||
|
||||
return nccl_NUM_TYPES;
|
||||
}
|
||||
|
||||
size_t wordSize(ncclDataType_t type) {
|
||||
switch(type) {
|
||||
case ncclChar: return sizeof(char);
|
||||
case ncclInt: return sizeof(int);
|
||||
#ifdef CUDA_HAS_HALF
|
||||
case ncclHalf: return sizeof(short);
|
||||
#endif
|
||||
case ncclFloat: return sizeof(float);
|
||||
case ncclDouble: return sizeof(double);
|
||||
case ncclInt64: return sizeof(long long);
|
||||
case ncclUint64: return sizeof(unsigned long long);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
double deltaMaxValue(ncclDataType_t type, bool is_reduction) {
|
||||
if (is_reduction) {
|
||||
switch(type) {
|
||||
#ifdef CUDA_HAS_HALF
|
||||
case ncclHalf: return 5e-2;
|
||||
#endif
|
||||
case ncclFloat: return 1e-5;
|
||||
case ncclDouble: return 1e-12;
|
||||
}
|
||||
}
|
||||
return 1e-200;
|
||||
}
|
||||
|
||||
ncclRedOp_t strToOp(const char* s) {
|
||||
if (strcmp(s, "sum") == 0)
|
||||
return ncclSum;
|
||||
if (strcmp(s, "prod") == 0)
|
||||
return ncclProd;
|
||||
if (strcmp(s, "max") == 0)
|
||||
return ncclMax;
|
||||
if (strcmp(s, "min") == 0)
|
||||
return ncclMin;
|
||||
|
||||
return nccl_NUM_OPS;
|
||||
}
|
||||
|
||||
int strToPosInt(const char* s) {
|
||||
errno = 0;
|
||||
long temp = strtol(s, NULL, 10);
|
||||
if (errno != 0 || temp > INT_MAX || temp < 0)
|
||||
return 0;
|
||||
return (int)temp;
|
||||
}
|
||||
|
||||
int strToNonNeg(const char* s) {
|
||||
errno = 0;
|
||||
long temp = strtol(s, NULL, 10);
|
||||
if (errno != 0 || temp > INT_MAX || temp < 0)
|
||||
return -1;
|
||||
return (int)temp;
|
||||
}
|
||||
|
||||
#endif // SRC_TEST_UTILITIES_H_
|
||||
@@ -1,93 +0,0 @@
|
||||
/*************************************************************************
|
||||
* Copyright (c) 2015-2016, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* See LICENCE.txt for license information
|
||||
************************************************************************/
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "nccl.h"
|
||||
#include "mpi.h"
|
||||
#include "test_utilities.h"
|
||||
|
||||
#define SIZE 128
|
||||
#define NITERS 1
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
ncclUniqueId commId;
|
||||
int size, rank;
|
||||
ncclResult_t ret;
|
||||
|
||||
MPI_Init(&argc, &argv);
|
||||
MPI_Comm_size(MPI_COMM_WORLD, &size);
|
||||
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
|
||||
|
||||
if (argc < size) {
|
||||
if (rank == 0)
|
||||
printf("Usage : %s <GPU list per rank>\n", argv[0]);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
int gpu = atoi(argv[rank+1]);
|
||||
|
||||
// We have to set our device before NCCL init
|
||||
CUDACHECK(cudaSetDevice(gpu));
|
||||
MPI_Barrier(MPI_COMM_WORLD);
|
||||
|
||||
// NCCL Communicator creation
|
||||
ncclComm_t comm;
|
||||
NCCLCHECK(ncclGetUniqueId(&commId));
|
||||
MPI_Bcast(&commId, NCCL_UNIQUE_ID_BYTES, MPI_CHAR, 0, MPI_COMM_WORLD);
|
||||
ret = ncclCommInitRank(&comm, size, commId, rank);
|
||||
if (ret != ncclSuccess) {
|
||||
printf("NCCL Init failed (%d) '%s'\n", ret, ncclGetErrorString(ret));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// CUDA stream creation
|
||||
cudaStream_t stream;
|
||||
CUDACHECK(cudaStreamCreateWithFlags(&stream, cudaStreamNonBlocking));
|
||||
|
||||
// Initialize input values
|
||||
int *dptr;
|
||||
CUDACHECK(cudaMalloc(&dptr, SIZE*2*sizeof(int)));
|
||||
int *val = (int*) malloc(SIZE*sizeof(int));
|
||||
for (int v=0; v<SIZE; v++) {
|
||||
val[v] = rank + 1;
|
||||
}
|
||||
CUDACHECK(cudaMemcpy(dptr, val, SIZE*sizeof(int), cudaMemcpyHostToDevice));
|
||||
|
||||
// Compute final value
|
||||
int ref = size*(size+1)/2;
|
||||
|
||||
// Run allreduce
|
||||
int errors = 0;
|
||||
for (int i=0; i<NITERS; i++) {
|
||||
NCCLCHECK(ncclAllReduce((const void*)dptr, (void*)(dptr+SIZE), SIZE, ncclInt, ncclSum, comm, stream));
|
||||
}
|
||||
|
||||
// Check results
|
||||
CUDACHECK(cudaStreamSynchronize(stream));
|
||||
CUDACHECK(cudaMemcpy(val, (dptr+SIZE), SIZE*sizeof(int), cudaMemcpyDeviceToHost));
|
||||
for (int v=0; v<SIZE; v++) {
|
||||
if (val[v] != ref) {
|
||||
errors++;
|
||||
printf("[%d] Error at %d : got %d instead of %d\n", rank, v, val[v], ref);
|
||||
}
|
||||
}
|
||||
CUDACHECK(cudaFree(dptr));
|
||||
|
||||
MPI_Allreduce(MPI_IN_PLACE, &errors, 1, MPI_INTEGER, MPI_SUM, MPI_COMM_WORLD);
|
||||
if (rank == 0) {
|
||||
if (errors)
|
||||
printf("%d errors. Test FAILED.\n", errors);
|
||||
else
|
||||
printf("Test PASSED.\n");
|
||||
}
|
||||
|
||||
MPI_Finalize();
|
||||
ncclCommDestroy(comm);
|
||||
return errors ? 1 : 0;
|
||||
}
|
||||
@@ -1,239 +0,0 @@
|
||||
/*************************************************************************
|
||||
* Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of NVIDIA CORPORATION nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
||||
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
************************************************************************/
|
||||
|
||||
#include <chrono>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <string>
|
||||
#include <float.h>
|
||||
|
||||
#include "nccl.h"
|
||||
#include "test_utilities.h"
|
||||
#include <nvToolsExt.h>
|
||||
|
||||
void showUsage(const char* bin) {
|
||||
printf("\n"
|
||||
"Usage: %s <type> <n_min> <n_max> [delta] [gpus] [gpu0 [gpu1 [...]]]\n"
|
||||
"Where:\n"
|
||||
#ifdef CUDA_HAS_HALF
|
||||
" type = [char|int|half|float|double|int64|uint64]\n"
|
||||
#else
|
||||
" type = [char|int|float|double|int64|uint64]\n"
|
||||
#endif
|
||||
" n_min > 0\n"
|
||||
" n_max >= n_min\n"
|
||||
" delta > 0\n\n", bin);
|
||||
return;
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
int nvis = 0;
|
||||
CUDACHECK(cudaGetDeviceCount(&nvis));
|
||||
if (nvis == 0) {
|
||||
printf("No GPUs found\n");
|
||||
showUsage(argv[0]);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
ncclDataType_t type;
|
||||
int n_min;
|
||||
int n_max;
|
||||
int delta;
|
||||
int gpus;
|
||||
int* list = NULL;
|
||||
|
||||
if (argc < 4) {
|
||||
showUsage(argv[0]);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
type = strToType(argv[1]);
|
||||
if (type == nccl_NUM_TYPES) {
|
||||
printf("Invalid <type> '%s'\n", argv[1]);
|
||||
showUsage(argv[0]);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
n_min = strToPosInt(argv[2]);
|
||||
if (n_min < 1) {
|
||||
printf("Invalid <n_min> '%s'\n", argv[2]);
|
||||
showUsage(argv[0]);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
n_max = strToPosInt(argv[3]);
|
||||
if (n_max < n_min) {
|
||||
printf("Invalid <n_max> '%s'\n", argv[3]);
|
||||
showUsage(argv[0]);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (argc > 4) {
|
||||
delta = strToPosInt(argv[4]);
|
||||
if (delta < 1) {
|
||||
printf("Invalid <delta> '%s'\n", argv[4]);
|
||||
showUsage(argv[0]);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
} else {
|
||||
delta = (n_max == n_min) ? 1 : (n_max - n_min+9) / 10;
|
||||
}
|
||||
|
||||
if (argc > 5) {
|
||||
gpus = strToPosInt(argv[5]);
|
||||
if (gpus < 1) {
|
||||
printf("Invalid <gpus> '%s'\n", argv[5]);
|
||||
showUsage(argv[0]);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
} else {
|
||||
gpus = nvis;
|
||||
}
|
||||
|
||||
list = (int*)malloc(gpus*sizeof(int));
|
||||
|
||||
if (argc > 6 && argc != 6+gpus) {
|
||||
printf("If given, GPU list must be fully specified.\n");
|
||||
showUsage(argv[0]);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
for(int g=0; g<gpus; ++g) {
|
||||
if(argc > 6) {
|
||||
list[g] = strToNonNeg(argv[6+g]);
|
||||
if (list[g] < 0) {
|
||||
printf("Invalid GPU%d '%s'\n", g, argv[6+g]);
|
||||
showUsage(argv[0]);
|
||||
exit(EXIT_FAILURE);
|
||||
} else if (list[g] >= nvis) {
|
||||
printf("GPU%d (%d) exceeds visible devices (%d)\n", g, list[g], nvis);
|
||||
showUsage(argv[0]);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
} else {
|
||||
list[g] = g % nvis;
|
||||
}
|
||||
}
|
||||
|
||||
size_t word = wordSize(type);
|
||||
size_t max_input = n_max * word;
|
||||
size_t max_output = max_input * gpus;
|
||||
void* refout;
|
||||
CUDACHECK(cudaMallocHost(&refout, max_output));
|
||||
|
||||
void **input, **output;
|
||||
double** localError;
|
||||
ncclComm_t* comm;
|
||||
cudaStream_t* stream;
|
||||
|
||||
input = (void**)malloc(gpus*sizeof(void*));
|
||||
output = (void**)malloc(gpus*sizeof(void*));
|
||||
localError = (double**)malloc(gpus*sizeof(double*));
|
||||
comm = (ncclComm_t*)malloc(gpus*sizeof(ncclComm_t));
|
||||
stream = (cudaStream_t*)malloc(gpus*sizeof(cudaStream_t));
|
||||
|
||||
for(int g=0; g<gpus; ++g) {
|
||||
char busid[32] = {0};
|
||||
CUDACHECK(cudaDeviceGetPCIBusId(busid, 32, list[g]));
|
||||
printf("# Rank %d using device %d [%s]\n", g, list[g], busid);
|
||||
|
||||
CUDACHECK(cudaSetDevice(list[g]));
|
||||
CUDACHECK(cudaMalloc(&input[g], max_input));
|
||||
CUDACHECK(cudaMalloc(&output[g], max_output));
|
||||
CUDACHECK(cudaMallocHost(&localError[g], sizeof(double)));
|
||||
CUDACHECK(cudaStreamCreate(&stream[g]));
|
||||
makeRandom(input[g], n_max, type, 42+g);
|
||||
|
||||
CUDACHECK(cudaMemcpy((char*)refout+max_input*g, input[g], max_input, cudaMemcpyDeviceToHost));
|
||||
}
|
||||
|
||||
NCCLCHECK(ncclCommInitAll(comm, gpus, list));
|
||||
|
||||
printf(" BYTES ERROR MSEC BW\n");
|
||||
|
||||
for(int n=n_min; n<=n_max; n+=delta) {
|
||||
size_t out_bytes = word * n * gpus;
|
||||
|
||||
for(int g=0; g<gpus; ++g) {
|
||||
CUDACHECK(cudaSetDevice(list[g]));
|
||||
CUDACHECK(cudaMemsetAsync(output[g], 0, out_bytes, stream[g]));
|
||||
CUDACHECK(cudaStreamSynchronize(stream[g]));
|
||||
}
|
||||
|
||||
auto start = std::chrono::high_resolution_clock::now();
|
||||
for(int g=0; g<gpus; ++g) {
|
||||
CUDACHECK(cudaSetDevice(list[g]));
|
||||
NCCLCHECK(ncclAllGather(input[g], n, type, output[g], comm[g], stream[g]));
|
||||
}
|
||||
for(int g=0; g<gpus; ++g) {
|
||||
CUDACHECK(cudaSetDevice(list[g]));
|
||||
CUDACHECK(cudaStreamSynchronize(stream[g]));
|
||||
}
|
||||
auto stop = std::chrono::high_resolution_clock::now();
|
||||
double ms = std::chrono::duration_cast<std::chrono::duration<double>>
|
||||
(stop - start).count() * 1000.0;
|
||||
|
||||
double max_error = 0.0;
|
||||
for(int slice=0; slice<gpus; ++slice) {
|
||||
void* refSlice = (void*)((char*)refout + slice*max_input);
|
||||
for(int g=0; g<gpus; ++g) {
|
||||
CUDACHECK(cudaSetDevice(list[g]));
|
||||
void* mySlice = (void*)((char*)output[g] + slice*n*word);
|
||||
maxDiff(localError[g], mySlice, refSlice, n, type, stream[g]);
|
||||
}
|
||||
for(int g=0; g<gpus; ++g) {
|
||||
CUDACHECK(cudaSetDevice(list[g]));
|
||||
CUDACHECK(cudaStreamSynchronize(stream[g]));
|
||||
max_error = max(max_error, *localError[g]);
|
||||
}
|
||||
}
|
||||
|
||||
double mb = (double)(n*word * (gpus-1)) * 1.e-6;
|
||||
double algbw = mb / ms;
|
||||
printf("%12lu %5.0le %10.3lf %6.2lf\n",
|
||||
n*word, max_error, ms, algbw);
|
||||
}
|
||||
|
||||
for(int g=0; g<gpus; ++g) {
|
||||
CUDACHECK(cudaSetDevice(list[g]));
|
||||
CUDACHECK(cudaStreamDestroy(stream[g]));
|
||||
ncclCommDestroy(comm[g]);
|
||||
CUDACHECK(cudaFree(input[g]));
|
||||
CUDACHECK(cudaFree(output[g]));
|
||||
CUDACHECK(cudaFreeHost(localError[g]));
|
||||
}
|
||||
|
||||
free(localError);
|
||||
free(output);
|
||||
free(input);
|
||||
free(comm);
|
||||
free(stream);
|
||||
CUDACHECK(cudaFreeHost(refout));
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
@@ -1,235 +0,0 @@
|
||||
/*************************************************************************
|
||||
* Copyright (c) 2015-2016, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* See LICENCE.txt for license information
|
||||
************************************************************************/
|
||||
|
||||
#include <chrono>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "nccl.h"
|
||||
#include "test_utilities.h"
|
||||
|
||||
int errors = 0;
|
||||
double avg_bw = 0.0;
|
||||
int avg_count = 0;
|
||||
bool is_reduction = false;
|
||||
|
||||
template<typename T>
|
||||
void RunTest(T** sendbuff, T** recvbuff, const int N, const ncclDataType_t type,
|
||||
ncclComm_t* const comms, const std::vector<int>& dList) {
|
||||
// initialize data
|
||||
int nDev = 0;
|
||||
NCCLCHECK(ncclCommCount(comms[0], &nDev));
|
||||
cudaStream_t* s = (cudaStream_t*)malloc(sizeof(cudaStream_t)*nDev);
|
||||
T* buffer = (T*)malloc(nDev * N * sizeof(T));
|
||||
T* result = (T*)malloc(nDev * N * sizeof(T));
|
||||
memset(buffer, 0, nDev * N * sizeof(T));
|
||||
memset(result, 0, nDev * N * sizeof(T));
|
||||
|
||||
for (int i = 0; i < nDev; ++i) {
|
||||
CUDACHECK(cudaSetDevice(dList[i]));
|
||||
CUDACHECK(cudaStreamCreate(s+i));
|
||||
CUDACHECK(cudaMemset(recvbuff[i], 0, nDev * N * sizeof(T)));
|
||||
Randomize(sendbuff[i], N, i);
|
||||
|
||||
CUDACHECK(cudaMemcpy(result + i * N, sendbuff[i], N * sizeof(T),
|
||||
cudaMemcpyDeviceToHost));
|
||||
}
|
||||
|
||||
// warm up GPU
|
||||
for (int i = 0; i < nDev; ++i) {
|
||||
CUDACHECK(cudaSetDevice(dList[i]));
|
||||
NCCLCHECK(ncclAllGather((const void*)sendbuff[i], std::min(32 * 1024, N), type,
|
||||
(void*)recvbuff[i], comms[i], s[i]));
|
||||
}
|
||||
|
||||
for (int i = 0; i < nDev; ++i) {
|
||||
CUDACHECK(cudaSetDevice(dList[i]));
|
||||
CUDACHECK(cudaStreamSynchronize(s[i]));
|
||||
}
|
||||
|
||||
//for (int n = 1; n <= N; n = n << 1)
|
||||
{
|
||||
int n = N;
|
||||
printf("%12i %12i %6s", (int)(n * sizeof(T)), n, TypeName(type).c_str());
|
||||
|
||||
auto start = std::chrono::high_resolution_clock::now();
|
||||
|
||||
for (int i = 0; i < nDev; ++i) {
|
||||
CUDACHECK(cudaSetDevice(dList[i]));
|
||||
NCCLCHECK(ncclAllGather((const void*)sendbuff[i], n, type, (void*)recvbuff[i], comms[i],
|
||||
s[i]));
|
||||
}
|
||||
|
||||
for (int i = 0; i < nDev; ++i) {
|
||||
CUDACHECK(cudaSetDevice(dList[i]));
|
||||
CUDACHECK(cudaStreamSynchronize(s[i]));
|
||||
}
|
||||
|
||||
auto stop = std::chrono::high_resolution_clock::now();
|
||||
|
||||
double elapsedSec =
|
||||
std::chrono::duration_cast<std::chrono::duration<double>>(
|
||||
stop - start).count();
|
||||
double algbw = (double)(n * sizeof(T)) / 1.0E9 * (double)(nDev - 1)
|
||||
/ elapsedSec;
|
||||
double busbw = algbw;
|
||||
|
||||
double maxDelta = 0.0;
|
||||
for (int i = 0; i < nDev; ++i) {
|
||||
CUDACHECK(cudaSetDevice(dList[i]));
|
||||
double tmpDelta = CheckDelta<T>(recvbuff[i], result, nDev*N);
|
||||
maxDelta = std::max(tmpDelta, maxDelta);
|
||||
}
|
||||
|
||||
printf(" %7.3f %5.2f %5.2f %7.0le\n", elapsedSec * 1.0E3, algbw, busbw,
|
||||
maxDelta);
|
||||
|
||||
if (maxDelta > deltaMaxValue(type, is_reduction)) errors++;
|
||||
avg_bw += busbw;
|
||||
avg_count++;
|
||||
|
||||
}
|
||||
|
||||
for (int i = 0; i < nDev; ++i) {
|
||||
CUDACHECK(cudaSetDevice(dList[i]));
|
||||
CUDACHECK(cudaStreamDestroy(s[i]));
|
||||
}
|
||||
free(s);
|
||||
free(buffer);
|
||||
free(result);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void RunTests(const int N, const ncclDataType_t type, ncclComm_t* const comms,
|
||||
const std::vector<int>& dList) {
|
||||
int nDev = 0;
|
||||
NCCLCHECK(ncclCommCount(comms[0], &nDev));
|
||||
T** sendbuff = (T**)malloc(nDev * sizeof(T*));
|
||||
T** recvbuff = (T**)malloc(nDev * sizeof(T*));
|
||||
|
||||
for (int i = 0; i < nDev; ++i) {
|
||||
CUDACHECK(cudaSetDevice(dList[i]));
|
||||
CUDACHECK(cudaMalloc(sendbuff + i, N * sizeof(T)));
|
||||
CUDACHECK(cudaMalloc(recvbuff + i, nDev * N * sizeof(T)));
|
||||
}
|
||||
|
||||
RunTest<T>(sendbuff, recvbuff, N, type, comms, dList);
|
||||
|
||||
for (int i = 0; i < nDev; ++i) {
|
||||
CUDACHECK(cudaSetDevice(dList[i]));
|
||||
CUDACHECK(cudaFree(sendbuff[i]));
|
||||
CUDACHECK(cudaFree(recvbuff[i]));
|
||||
}
|
||||
|
||||
free(sendbuff);
|
||||
free(recvbuff);
|
||||
}
|
||||
|
||||
void usage() {
|
||||
printf("Tests nccl AllGather with user supplied arguments.\n"
|
||||
" Usage: all_reduce_test <data size in bytes> [number of GPUs] "
|
||||
"[GPU 0] [GPU 1] ...\n\n");
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
int nVis = 0;
|
||||
CUDACHECK(cudaGetDeviceCount(&nVis));
|
||||
|
||||
int N = 0;
|
||||
if (argc > 1) {
|
||||
int t = sscanf(argv[1], "%d", &N);
|
||||
if (t == 0) {
|
||||
printf("Error: %s is not an integer!\n\n", argv[1]);
|
||||
usage();
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
} else {
|
||||
printf("Error: must specify at least data size in bytes!\n\n");
|
||||
usage();
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
int nDev = nVis;
|
||||
if (argc > 2) {
|
||||
int t = sscanf(argv[2], "%d", &nDev);
|
||||
if (t == 0) {
|
||||
printf("Error: %s is not an integer!\n\n", argv[1]);
|
||||
usage();
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
std::vector<int> dList(nDev);
|
||||
for (int i = 0; i < nDev; ++i)
|
||||
dList[i] = i % nVis;
|
||||
|
||||
|
||||
if (argc > 3) {
|
||||
if (argc - 3 != nDev) {
|
||||
printf("Error: insufficient number of GPUs in list\n\n");
|
||||
usage();
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
for (int i = 0; i < nDev; ++i) {
|
||||
int t = sscanf(argv[3 + i], "%d", dList.data() + i);
|
||||
if (t == 0) {
|
||||
printf("Error: %s is not an integer!\n\n", argv[2 + i]);
|
||||
usage();
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ncclComm_t* comms = (ncclComm_t*)malloc(sizeof(ncclComm_t)*nDev);
|
||||
NCCLCHECK(ncclCommInitAll(comms, nDev, dList.data()));
|
||||
|
||||
printf("# Using devices\n");
|
||||
for (int g=0; g<nDev; ++g) {
|
||||
int cudaDev;
|
||||
int rank;
|
||||
cudaDeviceProp prop;
|
||||
NCCLCHECK(ncclCommCuDevice(comms[g], &cudaDev));
|
||||
NCCLCHECK(ncclCommUserRank(comms[g], &rank));
|
||||
CUDACHECK(cudaGetDeviceProperties(&prop, cudaDev));
|
||||
printf("# Rank %2d uses device %2d [0x%02x] %s\n", rank, cudaDev,
|
||||
prop.pciBusID, prop.name);
|
||||
}
|
||||
printf("\n");
|
||||
|
||||
printf("# %10s %12s %6s %7s %5s %5s %7s\n",
|
||||
"bytes", "N", "type", "time", "algbw", "busbw", "delta");
|
||||
|
||||
RunTests<char>(N / sizeof(char), ncclChar, comms, dList);
|
||||
RunTests<int>(N / sizeof(int), ncclInt, comms, dList);
|
||||
#ifdef CUDA_HAS_HALF
|
||||
RunTests<half>(N / sizeof(half), ncclHalf, comms, dList);
|
||||
#endif
|
||||
RunTests<float>(N / sizeof(float), ncclFloat, comms, dList);
|
||||
RunTests<double>(N / sizeof(double), ncclDouble, comms, dList);
|
||||
RunTests<long long>(N / sizeof(long long), ncclInt64, comms, dList);
|
||||
RunTests<unsigned long long>(N / sizeof(unsigned long long), ncclUint64, comms, dList);
|
||||
|
||||
printf("\n");
|
||||
|
||||
for(int i=0; i<nDev; ++i)
|
||||
ncclCommDestroy(comms[i]);
|
||||
free(comms);
|
||||
|
||||
char* str = getenv("NCCL_TESTS_MIN_BW");
|
||||
double check_avg_bw = str ? atof(str) : -1;
|
||||
avg_bw /= avg_count;
|
||||
|
||||
printf(" Out of bounds values : %d %s\n", errors, errors ? "FAILED" : "OK");
|
||||
printf(" Avg bus bandwidth : %g %s\n", avg_bw, check_avg_bw == -1 ? "" : (avg_bw < check_avg_bw ? "FAILED" : "OK"));
|
||||
printf("\n");
|
||||
if (errors || avg_bw < check_avg_bw)
|
||||
exit(EXIT_FAILURE);
|
||||
else
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
@@ -1,247 +0,0 @@
|
||||
/*************************************************************************
|
||||
* Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of NVIDIA CORPORATION nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
||||
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
************************************************************************/
|
||||
|
||||
#include <chrono>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <string>
|
||||
#include <float.h>
|
||||
|
||||
#include "nccl.h"
|
||||
#include "test_utilities.h"
|
||||
#include <nvToolsExt.h>
|
||||
|
||||
void showUsage(const char* bin) {
|
||||
printf("\n"
|
||||
"Usage: %s <type> <op> <n_min> <n_max> [delta] [gpus] [gpu0 [gpu1 [...]]]\n"
|
||||
"Where:\n"
|
||||
#ifdef CUDA_HAS_HALF
|
||||
" type = [char|int|half|float|double|int64|uint64]\n"
|
||||
#else
|
||||
" type = [char|int|float|double|int64|uint64]\n"
|
||||
#endif
|
||||
" op = [sum|prod|max|min]\n"
|
||||
" n_min > 0\n"
|
||||
" n_max >= n_min\n"
|
||||
" delta > 0\n\n", bin);
|
||||
return;
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
int nvis = 0;
|
||||
CUDACHECK(cudaGetDeviceCount(&nvis));
|
||||
if (nvis == 0) {
|
||||
printf("No GPUs found\n");
|
||||
showUsage(argv[0]);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
ncclDataType_t type;
|
||||
ncclRedOp_t op;
|
||||
int n_min;
|
||||
int n_max;
|
||||
int delta;
|
||||
int gpus;
|
||||
int* list = NULL;
|
||||
|
||||
if (argc < 5) {
|
||||
showUsage(argv[0]);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
type = strToType(argv[1]);
|
||||
if (type == nccl_NUM_TYPES) {
|
||||
printf("Invalid <type> '%s'\n", argv[1]);
|
||||
showUsage(argv[0]);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
op = strToOp(argv[2]);
|
||||
if (op == nccl_NUM_OPS) {
|
||||
printf("Invalid <op> '%s'\n", argv[2]);
|
||||
showUsage(argv[0]);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
n_min = strToPosInt(argv[3]);
|
||||
if (n_min < 1) {
|
||||
printf("Invalid <n_min> '%s'\n", argv[3]);
|
||||
showUsage(argv[0]);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
n_max = strToPosInt(argv[4]);
|
||||
if (n_max < n_min) {
|
||||
printf("Invalid <n_max> '%s'\n", argv[4]);
|
||||
showUsage(argv[0]);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (argc > 5) {
|
||||
delta = strToPosInt(argv[5]);
|
||||
if (delta < 1) {
|
||||
printf("Invalid <delta> '%s'\n", argv[5]);
|
||||
showUsage(argv[0]);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
} else {
|
||||
delta = (n_max == n_min) ? 1 : (n_max - n_min+9) / 10;
|
||||
}
|
||||
|
||||
if (argc > 6) {
|
||||
gpus = strToPosInt(argv[6]);
|
||||
if (gpus < 1) {
|
||||
printf("Invalid <gpus> '%s'\n", argv[6]);
|
||||
showUsage(argv[0]);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
} else {
|
||||
gpus = nvis;
|
||||
}
|
||||
|
||||
list = (int*)malloc(gpus*sizeof(int));
|
||||
|
||||
if (argc > 7 && argc != 7+gpus) {
|
||||
printf("If given, GPU list must be fully specified.\n");
|
||||
showUsage(argv[0]);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
for(int g=0; g<gpus; ++g) {
|
||||
if(argc > 7) {
|
||||
list[g] = strToNonNeg(argv[7+g]);
|
||||
if (list[g] < 0) {
|
||||
printf("Invalid GPU%d '%s'\n", g, argv[7+g]);
|
||||
showUsage(argv[0]);
|
||||
exit(EXIT_FAILURE);
|
||||
} else if (list[g] >= nvis) {
|
||||
printf("GPU%d (%d) exceeds visible devices (%d)\n", g, list[g], nvis);
|
||||
showUsage(argv[0]);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
} else {
|
||||
list[g] = g % nvis;
|
||||
}
|
||||
}
|
||||
|
||||
size_t word = wordSize(type);
|
||||
size_t max_size = n_max * word;
|
||||
void* refout;
|
||||
CUDACHECK(cudaMallocHost(&refout, max_size));
|
||||
|
||||
void **input, **output;
|
||||
double** localError;
|
||||
ncclComm_t* comm;
|
||||
cudaStream_t* stream;
|
||||
|
||||
input = (void**)malloc(gpus*sizeof(void*));
|
||||
output = (void**)malloc(gpus*sizeof(void*));
|
||||
localError = (double**)malloc(gpus*sizeof(double*));
|
||||
comm = (ncclComm_t*)malloc(gpus*sizeof(ncclComm_t));
|
||||
stream = (cudaStream_t*)malloc(gpus*sizeof(cudaStream_t));
|
||||
|
||||
for(int g=0; g<gpus; ++g) {
|
||||
char busid[32] = {0};
|
||||
CUDACHECK(cudaDeviceGetPCIBusId(busid, 32, list[g]));
|
||||
printf("# Rank %d using device %d [%s]\n", g, list[g], busid);
|
||||
|
||||
CUDACHECK(cudaSetDevice(list[g]));
|
||||
CUDACHECK(cudaMalloc(&input[g], max_size));
|
||||
CUDACHECK(cudaMalloc(&output[g], max_size));
|
||||
CUDACHECK(cudaMallocHost(&localError[g], sizeof(double)));
|
||||
CUDACHECK(cudaStreamCreate(&stream[g]));
|
||||
makeRandom(input[g], n_max, type, 42+g);
|
||||
|
||||
if (g == 0)
|
||||
CUDACHECK(cudaMemcpy(refout, input[g], max_size, cudaMemcpyDeviceToHost));
|
||||
else
|
||||
accVec(refout, input[g], n_max, type, op);
|
||||
}
|
||||
|
||||
NCCLCHECK(ncclCommInitAll(comm, gpus, list));
|
||||
|
||||
printf(" BYTES ERROR MSEC ALGBW BUSBW\n");
|
||||
|
||||
for(int n=n_min; n<=n_max; n+=delta) {
|
||||
size_t bytes = word * n;
|
||||
|
||||
for(int g=0; g<gpus; ++g) {
|
||||
CUDACHECK(cudaSetDevice(list[g]));
|
||||
CUDACHECK(cudaMemsetAsync(output[g], 0, bytes, stream[g]));
|
||||
CUDACHECK(cudaStreamSynchronize(stream[g]));
|
||||
}
|
||||
|
||||
auto start = std::chrono::high_resolution_clock::now();
|
||||
for(int g=0; g<gpus; ++g) {
|
||||
CUDACHECK(cudaSetDevice(list[g]));
|
||||
NCCLCHECK(ncclAllReduce(input[g], output[g], n, type, op, comm[g], stream[g]));
|
||||
}
|
||||
for(int g=0; g<gpus; ++g) {
|
||||
CUDACHECK(cudaSetDevice(list[g]));
|
||||
CUDACHECK(cudaStreamSynchronize(stream[g]));
|
||||
}
|
||||
auto stop = std::chrono::high_resolution_clock::now();
|
||||
double ms = std::chrono::duration_cast<std::chrono::duration<double>>
|
||||
(stop - start).count() * 1000.0;
|
||||
|
||||
double max_error = 0.0;
|
||||
for(int g=0; g<gpus; ++g) {
|
||||
CUDACHECK(cudaSetDevice(list[g]));
|
||||
maxDiff(localError[g], output[g], refout, n, type, stream[g]);
|
||||
}
|
||||
for(int g=0; g<gpus; ++g) {
|
||||
CUDACHECK(cudaSetDevice(list[g]));
|
||||
CUDACHECK(cudaStreamSynchronize(stream[g]));
|
||||
max_error = max(max_error, *localError[g]);
|
||||
}
|
||||
|
||||
double mb = (double)bytes * 1.e-6;
|
||||
double algbw = mb / ms;
|
||||
double busbw = algbw * (double)(2*gpus - 2) / (double)gpus;
|
||||
printf("%12lu %5.0le %10.3lf %6.2lf %6.2lf\n",
|
||||
n*word, max_error, ms, algbw, busbw);
|
||||
}
|
||||
|
||||
for(int g=0; g<gpus; ++g) {
|
||||
CUDACHECK(cudaSetDevice(list[g]));
|
||||
CUDACHECK(cudaStreamDestroy(stream[g]));
|
||||
ncclCommDestroy(comm[g]);
|
||||
CUDACHECK(cudaFree(input[g]));
|
||||
CUDACHECK(cudaFree(output[g]));
|
||||
CUDACHECK(cudaFreeHost(localError[g]));
|
||||
}
|
||||
|
||||
free(localError);
|
||||
free(output);
|
||||
free(input);
|
||||
free(comm);
|
||||
free(stream);
|
||||
CUDACHECK(cudaFreeHost(refout));
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
@@ -1,301 +0,0 @@
|
||||
/*************************************************************************
|
||||
* Copyright (c) 2015-2016, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* See LICENCE.txt for license information
|
||||
************************************************************************/
|
||||
|
||||
#include <chrono>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "nccl.h"
|
||||
#include "test_utilities.h"
|
||||
#include <nvToolsExt.h>
|
||||
|
||||
int csv = false;
|
||||
int errors = 0;
|
||||
double avg_bw = 0.0;
|
||||
int avg_count = 0;
|
||||
bool is_reduction = true;
|
||||
|
||||
template<typename T>
|
||||
void RunTest(T** sendbuff, T** recvbuff, const int N, const ncclDataType_t type,
|
||||
const ncclRedOp_t op, ncclComm_t* comms, const std::vector<int>& dList) {
|
||||
// initialize data
|
||||
T* buffer = (T*)malloc(N * sizeof(T));
|
||||
T* result = (T*)malloc(N * sizeof(T));
|
||||
memset(buffer, 0, N * sizeof(T));
|
||||
memset(result, 0, N * sizeof(T));
|
||||
|
||||
int nDev = 0;
|
||||
NCCLCHECK(ncclCommCount(comms[0], &nDev));
|
||||
cudaStream_t* s = (cudaStream_t*)malloc(sizeof(cudaStream_t)*nDev);
|
||||
|
||||
for (int i = 0; i < nDev; ++i) {
|
||||
CUDACHECK(cudaSetDevice(dList[i]));
|
||||
CUDACHECK(cudaStreamCreate(s+i));
|
||||
CUDACHECK(cudaMemset(recvbuff[i], 0, N * sizeof(T)));
|
||||
Randomize(sendbuff[i], N, i);
|
||||
if(i == 0) {
|
||||
CUDACHECK(cudaMemcpy(result, sendbuff[i], N*sizeof(T), cudaMemcpyDeviceToHost));
|
||||
} else {
|
||||
Accumulate<T>(result, sendbuff[i], N, op);
|
||||
}
|
||||
}
|
||||
|
||||
// warm up GPU
|
||||
for (int i = 0; i < nDev; ++i) {
|
||||
CUDACHECK(cudaSetDevice(dList[i]));
|
||||
NCCLCHECK(ncclAllReduce((const void*)sendbuff[i], (void*)recvbuff[i], std::min(N, 1024 * 1024), type, op, comms[i], s[i]));
|
||||
}
|
||||
|
||||
for (int i = 0; i < nDev; ++i) {
|
||||
CUDACHECK(cudaSetDevice(dList[i]));
|
||||
CUDACHECK(cudaStreamSynchronize(s[i]));
|
||||
}
|
||||
|
||||
// for (int n = 0; n <= N; n = (n > 0) ? n << 1 : 1)
|
||||
{
|
||||
int n = N;
|
||||
printf((csv) ? "%i,%i,%s,%s," : "%12i %12i %6s %6s",
|
||||
(int) (n * sizeof(T)), n, TypeName(type).c_str(),
|
||||
OperationName(op).c_str());
|
||||
|
||||
// do out-of-place reduction first
|
||||
nvtxRangePushA("out of place");
|
||||
auto start = std::chrono::high_resolution_clock::now();
|
||||
//for (int i=0; i<100; i++) {
|
||||
for (int i = 0; i < nDev; ++i) {
|
||||
CUDACHECK(cudaSetDevice(dList[i]));
|
||||
NCCLCHECK(ncclAllReduce((const void*)sendbuff[i], (void*)recvbuff[i], n, type, op,
|
||||
comms[i], s[i]));
|
||||
}
|
||||
//}
|
||||
|
||||
for (int i = 0; i < nDev; ++i) {
|
||||
CUDACHECK(cudaSetDevice(dList[i]));
|
||||
CUDACHECK(cudaStreamSynchronize(s[i]));
|
||||
}
|
||||
|
||||
auto stop = std::chrono::high_resolution_clock::now();
|
||||
nvtxRangePop();
|
||||
|
||||
nvtxRangePushA("out of place bookkeeping");
|
||||
double elapsedSec =
|
||||
std::chrono::duration_cast<std::chrono::duration<double>>(
|
||||
stop - start).count(); // / 100.0;
|
||||
double algbw = (double)(n * sizeof(T)) / 1.0E9 / elapsedSec;
|
||||
double busbw = algbw * (double)(2 * nDev - 2) / (double)nDev;
|
||||
|
||||
double maxDelta = 0.0;
|
||||
for (int i = 0; i < nDev; ++i) {
|
||||
CUDACHECK(cudaSetDevice(dList[i]));
|
||||
double tmpDelta = CheckDelta<T>(recvbuff[i], result, N);
|
||||
maxDelta = std::max(tmpDelta, maxDelta);
|
||||
}
|
||||
|
||||
printf((csv)?"%f,%f,%f,%le,":" %7.3f %5.2f %5.2f %7.0le",
|
||||
elapsedSec * 1.0E3, algbw, busbw, maxDelta);
|
||||
|
||||
if (maxDelta > deltaMaxValue(type, is_reduction)) errors++;
|
||||
avg_bw += busbw;
|
||||
avg_count++;
|
||||
|
||||
nvtxRangePop();
|
||||
}
|
||||
|
||||
|
||||
// for (int n = 0; n <= N; n = (n > 0) ? n << 1 : 1)
|
||||
{
|
||||
int n = N;
|
||||
// now do in-place reduction
|
||||
nvtxRangePushA("in place");
|
||||
auto start = std::chrono::high_resolution_clock::now();
|
||||
//for (int i=0; i<100; i++) {
|
||||
for (int i = 0; i < nDev; ++i) {
|
||||
CUDACHECK(cudaSetDevice(dList[i]));
|
||||
NCCLCHECK(ncclAllReduce((const void*)sendbuff[i], (void*)sendbuff[i], n, type, op,
|
||||
comms[i], s[i]));
|
||||
}
|
||||
//}
|
||||
|
||||
for (int i = 0; i < nDev; ++i) {
|
||||
CUDACHECK(cudaSetDevice(dList[i]));
|
||||
CUDACHECK(cudaStreamSynchronize(s[i]));
|
||||
}
|
||||
|
||||
auto stop = std::chrono::high_resolution_clock::now();
|
||||
nvtxRangePop();
|
||||
|
||||
nvtxRangePushA("in place bookkeeping");
|
||||
double elapsedSec =
|
||||
std::chrono::duration_cast<std::chrono::duration<double>>(
|
||||
stop - start).count(); // / 100.0;
|
||||
double algbw = (double)(n * sizeof(T)) / 1.0E9 / elapsedSec;
|
||||
double busbw = algbw * (double)(2 * nDev - 2) / (double)nDev;
|
||||
|
||||
double maxDelta = 0.0;
|
||||
for (int i = 0; i < nDev; ++i) {
|
||||
CUDACHECK(cudaSetDevice(dList[i]));
|
||||
double tmpDelta = CheckDelta<T>(sendbuff[i], result, N);
|
||||
maxDelta = std::max(tmpDelta, maxDelta);
|
||||
}
|
||||
|
||||
printf((csv)?"%f,%f,%f,%le,":" %7.3f %5.2f %5.2f %7.0le\n",
|
||||
elapsedSec * 1.0E3, algbw, busbw, maxDelta);
|
||||
|
||||
if (maxDelta > deltaMaxValue(type, is_reduction)) errors++;
|
||||
avg_bw += busbw;
|
||||
avg_count++;
|
||||
|
||||
nvtxRangePop();
|
||||
}
|
||||
|
||||
for (int i = 0; i < nDev; ++i) {
|
||||
CUDACHECK(cudaSetDevice(dList[i]));
|
||||
CUDACHECK(cudaStreamDestroy(s[i]));
|
||||
}
|
||||
free(s);
|
||||
free(buffer);
|
||||
free(result);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void RunTests(const int N, const ncclDataType_t type, ncclComm_t* comms,
|
||||
const std::vector<int>& dList) {
|
||||
int nDev = 0;
|
||||
NCCLCHECK(ncclCommCount(comms[0], &nDev));
|
||||
T** sendbuff = (T**)malloc(nDev * sizeof(T*));
|
||||
T** recvbuff = (T**)malloc(nDev * sizeof(T*));
|
||||
|
||||
for (int i = 0; i < nDev; ++i) {
|
||||
CUDACHECK(cudaSetDevice(dList[i]));
|
||||
CUDACHECK(cudaMalloc(sendbuff + i, N * sizeof(T)));
|
||||
CUDACHECK(cudaMalloc(recvbuff + i, N * sizeof(T)));
|
||||
}
|
||||
|
||||
for (ncclRedOp_t op : { ncclSum, ncclProd, ncclMax, ncclMin }) {
|
||||
// for (ncclRedOp_t op : { ncclSum }) {
|
||||
RunTest<T>(sendbuff, recvbuff, N, type, op, comms, dList);
|
||||
}
|
||||
|
||||
for (int i = 0; i < nDev; ++i) {
|
||||
CUDACHECK(cudaSetDevice(dList[i]));
|
||||
CUDACHECK(cudaFree(sendbuff[i]));
|
||||
CUDACHECK(cudaFree(recvbuff[i]));
|
||||
}
|
||||
|
||||
free(sendbuff);
|
||||
free(recvbuff);
|
||||
}
|
||||
|
||||
void usage() {
|
||||
printf("Tests nccl AllReduce with user supplied arguments.\n"
|
||||
" Usage: all_reduce_test <data size in bytes> [number of GPUs] "
|
||||
"[GPU 0] [GPU 1] ...\n\n");
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
int nVis = 0;
|
||||
CUDACHECK(cudaGetDeviceCount(&nVis));
|
||||
|
||||
int N = 0;
|
||||
if (argc > 1) {
|
||||
int t = sscanf(argv[1], "%d", &N);
|
||||
if (t == 0) {
|
||||
printf("Error: %s is not an integer!\n\n", argv[1]);
|
||||
usage();
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
} else {
|
||||
printf("Error: must specify at least data size in bytes!\n\n");
|
||||
usage();
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
int nDev = nVis;
|
||||
if (argc > 2) {
|
||||
int t = sscanf(argv[2], "%d", &nDev);
|
||||
if (t == 0) {
|
||||
printf("Error: %s is not an integer!\n\n", argv[1]);
|
||||
usage();
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
std::vector<int> dList(nDev);
|
||||
for (int i = 0; i < nDev; ++i)
|
||||
dList[i] = i % nVis;
|
||||
|
||||
if (argc > 3) {
|
||||
if (argc - 3 != nDev) {
|
||||
printf("Error: insufficient number of GPUs in list\n\n");
|
||||
usage();
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
for (int i = 0; i < nDev; ++i) {
|
||||
int t = sscanf(argv[3 + i], "%d", dList.data() + i);
|
||||
if (t == 0) {
|
||||
printf("Error: %s is not an integer!\n\n", argv[2 + i]);
|
||||
usage();
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ncclComm_t* comms = (ncclComm_t*)malloc(sizeof(ncclComm_t)*nDev);
|
||||
NCCLCHECK(ncclCommInitAll(comms, nDev, dList.data()));
|
||||
|
||||
if (!csv) {
|
||||
printf("# Using devices\n");
|
||||
for (int g = 0; g < nDev; ++g) {
|
||||
int cudaDev;
|
||||
int rank;
|
||||
cudaDeviceProp prop;
|
||||
NCCLCHECK(ncclCommCuDevice(comms[g], &cudaDev));
|
||||
NCCLCHECK(ncclCommUserRank(comms[g], &rank));
|
||||
CUDACHECK(cudaGetDeviceProperties(&prop, cudaDev));
|
||||
printf("# Rank %2d uses device %2d [0x%02x] %s\n", rank, cudaDev,
|
||||
prop.pciBusID, prop.name);
|
||||
}
|
||||
printf("\n");
|
||||
|
||||
printf("# %10s %12s %6s %6s out-of-place in-place\n", "", "", "", "");
|
||||
printf("# %10s %12s %6s %6s %7s %5s %5s %7s %7s %5s %5s %7s\n", "bytes", "N", "type", "op",
|
||||
"time", "algbw", "busbw", "res", "time", "algbw", "busbw", "res");
|
||||
}
|
||||
else {
|
||||
printf("B,N,type,op,oop_time,oop_algbw,oop_busbw,oop_res,ip_time,ip_algbw,ip_busbw,ip_res\n");
|
||||
}
|
||||
|
||||
RunTests<char>(N / sizeof(char), ncclChar, comms, dList);
|
||||
RunTests<int>(N / sizeof(int), ncclInt, comms, dList);
|
||||
#ifdef CUDA_HAS_HALF
|
||||
RunTests<half>(N / sizeof(half), ncclHalf, comms, dList);
|
||||
#endif
|
||||
RunTests<float>(N / sizeof(float), ncclFloat, comms, dList);
|
||||
RunTests<double>(N / sizeof(double), ncclDouble, comms, dList);
|
||||
RunTests<long long>(N / sizeof(long long), ncclInt64, comms, dList);
|
||||
RunTests<unsigned long long>(N / sizeof(unsigned long long), ncclUint64, comms, dList);
|
||||
|
||||
printf("\n");
|
||||
|
||||
for(int i=0; i<nDev; ++i)
|
||||
ncclCommDestroy(comms[i]);
|
||||
free(comms);
|
||||
|
||||
char* str = getenv("NCCL_TESTS_MIN_BW");
|
||||
double check_avg_bw = str ? atof(str) : -1;
|
||||
avg_bw /= avg_count;
|
||||
|
||||
printf(" Out of bounds values : %d %s\n", errors, errors ? "FAILED" : "OK");
|
||||
printf(" Avg bus bandwidth : %g %s\n", avg_bw, check_avg_bw == -1 ? "" : (avg_bw < check_avg_bw ? "FAILED" : "OK"));
|
||||
printf("\n");
|
||||
if (errors || avg_bw < check_avg_bw)
|
||||
exit(EXIT_FAILURE);
|
||||
else
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
@@ -1,232 +0,0 @@
|
||||
/*************************************************************************
|
||||
* Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of NVIDIA CORPORATION nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
||||
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
************************************************************************/
|
||||
|
||||
#include <chrono>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <string>
|
||||
#include <float.h>
|
||||
|
||||
#include "nccl.h"
|
||||
#include "test_utilities.h"
|
||||
#include <nvToolsExt.h>
|
||||
|
||||
void showUsage(const char* bin) {
|
||||
printf("\n"
|
||||
"Usage: %s <type> <n_min> <n_max> [delta] [gpus] [gpu0 [gpu1 [...]]]\n"
|
||||
"Where:\n"
|
||||
#ifdef CUDA_HAS_HALF
|
||||
" type = [char|int|half|float|double|int64|uint64]\n"
|
||||
#else
|
||||
" type = [char|int|float|double|int64|uint64]\n"
|
||||
#endif
|
||||
" n_min > 0\n"
|
||||
" n_max >= n_min\n"
|
||||
" delta > 0\n\n", bin);
|
||||
return;
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
int nvis = 0;
|
||||
CUDACHECK(cudaGetDeviceCount(&nvis));
|
||||
if (nvis == 0) {
|
||||
printf("No GPUs found\n");
|
||||
showUsage(argv[0]);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
ncclDataType_t type;
|
||||
int n_min;
|
||||
int n_max;
|
||||
int delta;
|
||||
int gpus;
|
||||
int* list = NULL;
|
||||
|
||||
if (argc < 4) {
|
||||
showUsage(argv[0]);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
type = strToType(argv[1]);
|
||||
if (type == nccl_NUM_TYPES) {
|
||||
printf("Invalid <type> '%s'\n", argv[1]);
|
||||
showUsage(argv[0]);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
n_min = strToPosInt(argv[2]);
|
||||
if (n_min < 1) {
|
||||
printf("Invalid <n_min> '%s'\n", argv[2]);
|
||||
showUsage(argv[0]);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
n_max = strToPosInt(argv[3]);
|
||||
if (n_max < n_min) {
|
||||
printf("Invalid <n_max> '%s'\n", argv[3]);
|
||||
showUsage(argv[0]);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (argc > 4) {
|
||||
delta = strToPosInt(argv[4]);
|
||||
if (delta < 1) {
|
||||
printf("Invalid <delta> '%s'\n", argv[4]);
|
||||
showUsage(argv[0]);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
} else {
|
||||
delta = (n_max == n_min) ? 1 : (n_max - n_min+9) / 10;
|
||||
}
|
||||
|
||||
if (argc > 5) {
|
||||
gpus = strToPosInt(argv[5]);
|
||||
if (gpus < 1) {
|
||||
printf("Invalid <gpus> '%s'\n", argv[5]);
|
||||
showUsage(argv[0]);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
} else {
|
||||
gpus = nvis;
|
||||
}
|
||||
|
||||
list = (int*)malloc(gpus*sizeof(int));
|
||||
|
||||
if (argc > 6 && argc != 6+gpus) {
|
||||
printf("If given, GPU list must be fully specified.\n");
|
||||
showUsage(argv[0]);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
for(int g=0; g<gpus; ++g) {
|
||||
if(argc > 6) {
|
||||
list[g] = strToNonNeg(argv[6+g]);
|
||||
if (list[g] < 0) {
|
||||
printf("Invalid GPU%d '%s'\n", g, argv[6+g]);
|
||||
showUsage(argv[0]);
|
||||
exit(EXIT_FAILURE);
|
||||
} else if (list[g] >= nvis) {
|
||||
printf("GPU%d (%d) exceeds visible devices (%d)\n", g, list[g], nvis);
|
||||
showUsage(argv[0]);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
} else {
|
||||
list[g] = g % nvis;
|
||||
}
|
||||
}
|
||||
|
||||
size_t word = wordSize(type);
|
||||
size_t max_size = n_max * word;
|
||||
void* refout;
|
||||
CUDACHECK(cudaMallocHost(&refout, max_size));
|
||||
|
||||
void** io;
|
||||
double* localError;
|
||||
ncclComm_t* comm;
|
||||
cudaStream_t* stream;
|
||||
|
||||
io = (void**)malloc(gpus*sizeof(void*));
|
||||
CUDACHECK(cudaMallocHost(&localError, gpus*sizeof(double)));
|
||||
comm = (ncclComm_t*)malloc(gpus*sizeof(ncclComm_t));
|
||||
stream = (cudaStream_t*)malloc(gpus*sizeof(cudaStream_t));
|
||||
|
||||
for(int g=0; g<gpus; ++g) {
|
||||
char busid[32] = {0};
|
||||
CUDACHECK(cudaDeviceGetPCIBusId(busid, 32, list[g]));
|
||||
printf("# Rank %d using device %d [%s]\n", g, list[g], busid);
|
||||
|
||||
CUDACHECK(cudaSetDevice(list[g]));
|
||||
CUDACHECK(cudaStreamCreate(&stream[g]));
|
||||
CUDACHECK(cudaMalloc(&io[g], max_size));
|
||||
if(g == 0) {
|
||||
makeRandom(io[g], n_max, type, 42+g);
|
||||
CUDACHECK(cudaMemcpy(refout, io[g], max_size, cudaMemcpyDeviceToHost));
|
||||
}
|
||||
}
|
||||
|
||||
NCCLCHECK(ncclCommInitAll(comm, gpus, list));
|
||||
|
||||
printf(" BYTES ERROR MSEC BW\n");
|
||||
|
||||
for(int n=n_min; n<=n_max; n+=delta) {
|
||||
size_t bytes = word * n;
|
||||
|
||||
for(int g=1; g<gpus; ++g) {
|
||||
CUDACHECK(cudaSetDevice(list[g]));
|
||||
CUDACHECK(cudaMemsetAsync(io[g], 0, bytes, stream[g]));
|
||||
}
|
||||
for(int g=0; g<gpus; ++g) {
|
||||
CUDACHECK(cudaSetDevice(list[g]));
|
||||
CUDACHECK(cudaStreamSynchronize(stream[0]));
|
||||
}
|
||||
|
||||
auto start = std::chrono::high_resolution_clock::now();
|
||||
for(int g=0; g<gpus; ++g) {
|
||||
CUDACHECK(cudaSetDevice(list[g]));
|
||||
NCCLCHECK(ncclBcast(io[g], n, type, 0, comm[g], stream[g]));
|
||||
}
|
||||
for(int g=0; g<gpus; ++g) {
|
||||
CUDACHECK(cudaSetDevice(list[g]));
|
||||
CUDACHECK(cudaStreamSynchronize(stream[g]));
|
||||
}
|
||||
auto stop = std::chrono::high_resolution_clock::now();
|
||||
double ms = std::chrono::duration_cast<std::chrono::duration<double>>
|
||||
(stop - start).count() * 1000.0;
|
||||
|
||||
for(int g=1; g<gpus; ++g) {
|
||||
CUDACHECK(cudaSetDevice(list[g]));
|
||||
maxDiff(localError+g, io[g], refout, n, type, stream[g]);
|
||||
}
|
||||
double maxError = 0.0;
|
||||
for(int g=1; g<gpus; ++g) {
|
||||
CUDACHECK(cudaSetDevice(list[g]));
|
||||
CUDACHECK(cudaStreamSynchronize(stream[g]));
|
||||
maxError = max(maxError, localError[g]);
|
||||
}
|
||||
|
||||
double mb = (double)bytes * 1.e-6;
|
||||
double algbw = mb / ms;
|
||||
printf("%12lu %5.0le %10.3lf %6.2lf\n",
|
||||
n*word, maxError, ms, algbw);
|
||||
}
|
||||
|
||||
for(int g=0; g<gpus; ++g) {
|
||||
CUDACHECK(cudaSetDevice(list[g]));
|
||||
CUDACHECK(cudaStreamDestroy(stream[g]));
|
||||
ncclCommDestroy(comm[g]);
|
||||
CUDACHECK(cudaFree(io[g]));
|
||||
}
|
||||
|
||||
free(io);
|
||||
free(comm);
|
||||
free(stream);
|
||||
CUDACHECK(cudaFreeHost(refout));
|
||||
CUDACHECK(cudaFreeHost(localError));
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
@@ -1,235 +0,0 @@
|
||||
/*************************************************************************
|
||||
* Copyright (c) 2015-2016, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* See LICENCE.txt for license information
|
||||
************************************************************************/
|
||||
|
||||
#include <chrono>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "nccl.h"
|
||||
#include "test_utilities.h"
|
||||
|
||||
int errors = 0;
|
||||
double avg_bw = 0.0;
|
||||
int avg_count = 0;
|
||||
bool is_reduction = false;
|
||||
|
||||
template<typename T>
|
||||
void RunTest(T** buff, const int N, const ncclDataType_t type, const int root,
|
||||
ncclComm_t* const comms, const std::vector<int>& dList) {
|
||||
// initialize data
|
||||
int nDev = 0;
|
||||
NCCLCHECK(ncclCommCount(comms[0], &nDev));
|
||||
cudaStream_t* s = (cudaStream_t*)malloc(sizeof(cudaStream_t)*nDev);
|
||||
T* buffer = (T*)malloc(N * sizeof(T));
|
||||
T* result = (T*)malloc(N * sizeof(T));
|
||||
memset(result, 0, N * sizeof(T));
|
||||
|
||||
for (int i = 0; i < nDev; ++i) {
|
||||
CUDACHECK(cudaSetDevice(dList[i]));
|
||||
CUDACHECK(cudaStreamCreate(s+i));
|
||||
|
||||
if (i == root) {
|
||||
Randomize(buff[root], N, root);
|
||||
CUDACHECK(cudaMemcpy(result, buff[root], N * sizeof(T),
|
||||
cudaMemcpyDeviceToHost));
|
||||
} else {
|
||||
CUDACHECK(cudaMemset(buff[i], 0, N * sizeof(T)));
|
||||
}
|
||||
|
||||
CUDACHECK(cudaDeviceSynchronize());
|
||||
}
|
||||
|
||||
// warm up GPU
|
||||
for (int i = 0; i < nDev; ++i) {
|
||||
CUDACHECK(cudaSetDevice(dList[i]));
|
||||
NCCLCHECK(ncclBcast((void*)buff[i], std::min(32 * 1024, N), type, root, comms[i], s[i]));
|
||||
}
|
||||
|
||||
for (int i = 0; i < nDev; ++i) {
|
||||
CUDACHECK(cudaSetDevice(dList[i]));
|
||||
CUDACHECK(cudaStreamSynchronize(s[i]));
|
||||
}
|
||||
|
||||
// for (int n = 1; n <= N; n = n << 1)
|
||||
{
|
||||
int n = N;
|
||||
printf("%12i %12i %6s %4i", (int)(n * sizeof(T)), n,
|
||||
TypeName(type).c_str(), root);
|
||||
|
||||
auto start = std::chrono::high_resolution_clock::now();
|
||||
|
||||
for (int i = 0; i < nDev; ++i) {
|
||||
CUDACHECK(cudaSetDevice(dList[i]));
|
||||
NCCLCHECK(ncclBcast((void*)buff[i], n, type, root, comms[i], s[i]));
|
||||
}
|
||||
|
||||
for (int i = 0; i < nDev; ++i) {
|
||||
CUDACHECK(cudaSetDevice(dList[i]));
|
||||
CUDACHECK(cudaStreamSynchronize(s[i]));
|
||||
}
|
||||
|
||||
auto stop = std::chrono::high_resolution_clock::now();
|
||||
|
||||
double elapsedSec =
|
||||
std::chrono::duration_cast<std::chrono::duration<double>>(
|
||||
stop - start).count();
|
||||
double algbw = (double)(n * sizeof(T)) / 1.0E9 / elapsedSec;
|
||||
double busbw = algbw;
|
||||
|
||||
double maxDelta = 0.0;
|
||||
for (int i = 0; i < nDev; ++i) {
|
||||
CUDACHECK(cudaSetDevice(dList[i]));
|
||||
double tmpDelta = CheckDelta<T>(buff[i], result, n);
|
||||
maxDelta = std::max(tmpDelta, maxDelta);
|
||||
}
|
||||
|
||||
printf(" %7.3f %5.2f %5.2f %7.0le\n", elapsedSec * 1.0E3, algbw, busbw,
|
||||
maxDelta);
|
||||
|
||||
if (maxDelta > deltaMaxValue(type, is_reduction)) errors++;
|
||||
avg_bw += busbw;
|
||||
avg_count++;
|
||||
|
||||
}
|
||||
|
||||
for(int i=0; i < nDev; ++i) {
|
||||
CUDACHECK(cudaSetDevice(dList[i]));
|
||||
CUDACHECK(cudaStreamDestroy(s[i]));
|
||||
}
|
||||
free(s);
|
||||
free(buffer);
|
||||
free(result);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void RunTests(const int N, const ncclDataType_t type, ncclComm_t* const comms,
|
||||
const std::vector<int>& dList) {
|
||||
int nDev = 0;
|
||||
NCCLCHECK(ncclCommCount(comms[0], &nDev));
|
||||
T** buff = (T**)malloc(nDev * sizeof(T*));
|
||||
|
||||
for (int i = 0; i < nDev; ++i) {
|
||||
CUDACHECK(cudaSetDevice(dList[i]));
|
||||
CUDACHECK(cudaMalloc(buff + i, N * sizeof(T)));
|
||||
}
|
||||
|
||||
//for (int root = 1; root < 2; ++root) {
|
||||
for (int root = 0; root < nDev; ++root) {
|
||||
RunTest<T>(buff, N, type, root, comms, dList);
|
||||
}
|
||||
|
||||
for (int i = 0; i < nDev; ++i) {
|
||||
CUDACHECK(cudaSetDevice(dList[i]));
|
||||
CUDACHECK(cudaFree(buff[i]));
|
||||
}
|
||||
|
||||
free(buff);
|
||||
}
|
||||
|
||||
void usage() {
|
||||
printf("Tests nccl Broadcast with user supplied arguments.\n"
|
||||
" Usage: broadcast_test <data size in bytes> [number of GPUs] "
|
||||
"[GPU 0] [GPU 1] ...\n\n");
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
int nVis = 0;
|
||||
CUDACHECK(cudaGetDeviceCount(&nVis));
|
||||
|
||||
unsigned long long N = 0;
|
||||
if (argc > 1) {
|
||||
int t = sscanf(argv[1], "%llu", &N);
|
||||
if (t == 0) {
|
||||
printf("Error: %s is not an integer!\n\n", argv[1]);
|
||||
usage();
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
} else {
|
||||
printf("Error: must specify at least data size in bytes!\n\n");
|
||||
usage();
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
int nDev = nVis;
|
||||
if (argc > 2) {
|
||||
int t = sscanf(argv[2], "%d", &nDev);
|
||||
if (t == 0) {
|
||||
printf("Error: %s is not an integer!\n\n", argv[1]);
|
||||
usage();
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
std::vector<int> dList(nDev);
|
||||
for (int i = 0; i < nDev; ++i)
|
||||
dList[i] = i % nVis;
|
||||
|
||||
if (argc > 3) {
|
||||
if (argc - 3 != nDev) {
|
||||
printf("Error: insufficient number of GPUs in list\n\n");
|
||||
usage();
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
for (int i = 0; i < nDev; ++i) {
|
||||
int t = sscanf(argv[3 + i], "%d", dList.data() + i);
|
||||
if (t == 0) {
|
||||
printf("Error: %s is not an integer!\n\n", argv[2 + i]);
|
||||
usage();
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ncclComm_t* comms = (ncclComm_t*)malloc(sizeof(ncclComm_t)*nDev);;
|
||||
NCCLCHECK(ncclCommInitAll(comms, nDev, dList.data()));
|
||||
|
||||
printf("# Using devices\n");
|
||||
for (int g = 0; g < nDev; ++g) {
|
||||
int cudaDev;
|
||||
int rank;
|
||||
cudaDeviceProp prop;
|
||||
NCCLCHECK(ncclCommCuDevice(comms[g], &cudaDev));
|
||||
NCCLCHECK(ncclCommUserRank(comms[g], &rank));
|
||||
CUDACHECK(cudaGetDeviceProperties(&prop, cudaDev));
|
||||
printf("# Rank %2d uses device %2d [0x%02x] %s\n", rank, cudaDev,
|
||||
prop.pciBusID, prop.name);
|
||||
}
|
||||
printf("\n");
|
||||
|
||||
printf("# %10s %12s %6s %4s %7s %5s %5s %7s\n",
|
||||
"bytes", "N", "type", "root", "time", "algbw", "busbw", "delta");
|
||||
|
||||
RunTests<char>(N / sizeof(char), ncclChar, comms, dList);
|
||||
RunTests<int>(N / sizeof(int), ncclInt, comms, dList);
|
||||
#ifdef CUDA_HAS_HALF
|
||||
RunTests<half>(N / sizeof(half), ncclHalf, comms, dList);
|
||||
#endif
|
||||
RunTests<float>(N / sizeof(float), ncclFloat, comms, dList);
|
||||
RunTests<double>(N / sizeof(double), ncclDouble, comms, dList);
|
||||
RunTests<long long>(N / sizeof(long long), ncclInt64, comms, dList);
|
||||
RunTests<unsigned long long>(N / sizeof(unsigned long long), ncclUint64, comms, dList);
|
||||
|
||||
printf("\n");
|
||||
|
||||
for(int i = 0; i < nDev; ++i)
|
||||
ncclCommDestroy(comms[i]);
|
||||
free(comms);
|
||||
|
||||
char* str = getenv("NCCL_TESTS_MIN_BW");
|
||||
double check_avg_bw = str ? atof(str) : -1;
|
||||
avg_bw /= avg_count;
|
||||
|
||||
printf(" Out of bounds values : %d %s\n", errors, errors ? "FAILED" : "OK");
|
||||
printf(" Avg bus bandwidth : %g %s\n", avg_bw, check_avg_bw == -1 ? "" : (avg_bw < check_avg_bw ? "FAILED" : "OK"));
|
||||
printf("\n");
|
||||
if (errors || avg_bw < check_avg_bw)
|
||||
exit(EXIT_FAILURE);
|
||||
else
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
@@ -1,238 +0,0 @@
|
||||
/*************************************************************************
|
||||
* Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of NVIDIA CORPORATION nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
||||
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
************************************************************************/
|
||||
|
||||
#include <chrono>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <string>
|
||||
#include <float.h>
|
||||
|
||||
#include "nccl.h"
|
||||
#include "test_utilities.h"
|
||||
#include <nvToolsExt.h>
|
||||
|
||||
void showUsage(const char* bin) {
|
||||
printf("\n"
|
||||
"Usage: %s <type> <op> <n_min> <n_max> [delta] [gpus] [gpu0 [gpu1 [...]]]\n"
|
||||
"Where:\n"
|
||||
#ifdef CUDA_HAS_HALF
|
||||
" type = [char|int|half|float|double|int64|uint64]\n"
|
||||
#else
|
||||
" type = [char|int|float|double|int64|uint64]\n"
|
||||
#endif
|
||||
" op = [sum|prod|max|min]\n"
|
||||
" n_min > 0\n"
|
||||
" n_max >= n_min\n"
|
||||
" delta > 0\n\n", bin);
|
||||
return;
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
int nvis = 0;
|
||||
CUDACHECK(cudaGetDeviceCount(&nvis));
|
||||
if (nvis == 0) {
|
||||
printf("No GPUs found\n");
|
||||
showUsage(argv[0]);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
ncclDataType_t type;
|
||||
ncclRedOp_t op;
|
||||
int n_min;
|
||||
int n_max;
|
||||
int delta;
|
||||
int gpus;
|
||||
int* list = NULL;
|
||||
|
||||
if (argc < 5) {
|
||||
showUsage(argv[0]);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
type = strToType(argv[1]);
|
||||
if (type == nccl_NUM_TYPES) {
|
||||
printf("Invalid <type> '%s'\n", argv[1]);
|
||||
showUsage(argv[0]);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
op = strToOp(argv[2]);
|
||||
if (op == nccl_NUM_OPS) {
|
||||
printf("Invalid <op> '%s'\n", argv[2]);
|
||||
showUsage(argv[0]);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
n_min = strToPosInt(argv[3]);
|
||||
if (n_min < 1) {
|
||||
printf("Invalid <n_min> '%s'\n", argv[3]);
|
||||
showUsage(argv[0]);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
n_max = strToPosInt(argv[4]);
|
||||
if (n_max < n_min) {
|
||||
printf("Invalid <n_max> '%s'\n", argv[4]);
|
||||
showUsage(argv[0]);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (argc > 5) {
|
||||
delta = strToPosInt(argv[5]);
|
||||
if (delta < 1) {
|
||||
printf("Invalid <delta> '%s'\n", argv[5]);
|
||||
showUsage(argv[0]);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
} else {
|
||||
delta = (n_max == n_min) ? 1 : (n_max - n_min+9) / 10;
|
||||
}
|
||||
|
||||
if (argc > 6) {
|
||||
gpus = strToPosInt(argv[6]);
|
||||
if (gpus < 1) {
|
||||
printf("Invalid <gpus> '%s'\n", argv[6]);
|
||||
showUsage(argv[0]);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
} else {
|
||||
gpus = nvis;
|
||||
}
|
||||
|
||||
list = (int*)malloc(gpus*sizeof(int));
|
||||
|
||||
if (argc > 7 && argc != 7+gpus) {
|
||||
printf("If given, GPU list must be fully specified.\n");
|
||||
showUsage(argv[0]);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
for(int g=0; g<gpus; ++g) {
|
||||
if(argc > 7) {
|
||||
list[g] = strToNonNeg(argv[7+g]);
|
||||
if (list[g] < 0) {
|
||||
printf("Invalid GPU%d '%s'\n", g, argv[7+g]);
|
||||
showUsage(argv[0]);
|
||||
exit(EXIT_FAILURE);
|
||||
} else if (list[g] >= nvis) {
|
||||
printf("GPU%d (%d) exceeds visible devices (%d)\n", g, list[g], nvis);
|
||||
showUsage(argv[0]);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
} else {
|
||||
list[g] = g % nvis;
|
||||
}
|
||||
}
|
||||
|
||||
size_t word = wordSize(type);
|
||||
size_t max_size = n_max * word;
|
||||
void* refout;
|
||||
CUDACHECK(cudaMallocHost(&refout, max_size));
|
||||
|
||||
void** input;
|
||||
void* output; // always goes on rank 0
|
||||
double* maxError;
|
||||
ncclComm_t* comm;
|
||||
cudaStream_t* stream;
|
||||
|
||||
input = (void**)malloc(gpus*sizeof(void*));
|
||||
comm = (ncclComm_t*)malloc(gpus*sizeof(ncclComm_t));
|
||||
stream = (cudaStream_t*)malloc(gpus*sizeof(cudaStream_t));
|
||||
|
||||
for(int g=0; g<gpus; ++g) {
|
||||
char busid[32] = {0};
|
||||
CUDACHECK(cudaDeviceGetPCIBusId(busid, 32, list[g]));
|
||||
printf("# Rank %d using device %d [%s]\n", g, list[g], busid);
|
||||
|
||||
CUDACHECK(cudaSetDevice(list[g]));
|
||||
CUDACHECK(cudaStreamCreate(&stream[g]));
|
||||
CUDACHECK(cudaMalloc(&input[g], max_size));
|
||||
makeRandom(input[g], n_max, type, 42+g);
|
||||
|
||||
if (g == 0) {
|
||||
CUDACHECK(cudaMalloc(&output, max_size));
|
||||
CUDACHECK(cudaMallocHost(&maxError, sizeof(double)));
|
||||
CUDACHECK(cudaMemcpy(refout, input[g], max_size, cudaMemcpyDeviceToHost));
|
||||
} else {
|
||||
accVec(refout, input[g], n_max, type, op);
|
||||
}
|
||||
}
|
||||
|
||||
NCCLCHECK(ncclCommInitAll(comm, gpus, list));
|
||||
|
||||
printf(" BYTES ERROR MSEC BW\n");
|
||||
|
||||
for(int n=n_min; n<=n_max; n+=delta) {
|
||||
size_t bytes = word * n;
|
||||
|
||||
CUDACHECK(cudaSetDevice(list[0]));
|
||||
CUDACHECK(cudaMemsetAsync(output, 0, bytes, stream[0]));
|
||||
for(int g=0; g<gpus; ++g)
|
||||
CUDACHECK(cudaStreamSynchronize(stream[0]));
|
||||
|
||||
auto start = std::chrono::high_resolution_clock::now();
|
||||
for(int g=0; g<gpus; ++g) {
|
||||
CUDACHECK(cudaSetDevice(list[g]));
|
||||
NCCLCHECK(ncclReduce(input[g], output, n, type, op, 0, comm[g], stream[g]));
|
||||
}
|
||||
for(int g=0; g<gpus; ++g) {
|
||||
CUDACHECK(cudaSetDevice(list[g]));
|
||||
CUDACHECK(cudaStreamSynchronize(stream[g]));
|
||||
}
|
||||
auto stop = std::chrono::high_resolution_clock::now();
|
||||
double ms = std::chrono::duration_cast<std::chrono::duration<double>>
|
||||
(stop - start).count() * 1000.0;
|
||||
|
||||
CUDACHECK(cudaSetDevice(list[0]));
|
||||
maxDiff(maxError, output, refout, n, type, stream[0]);
|
||||
CUDACHECK(cudaStreamSynchronize(stream[0]));
|
||||
|
||||
double mb = (double)bytes * 1.e-6;
|
||||
double algbw = mb / ms;
|
||||
printf("%12lu %5.0le %10.3lf %6.2lf\n",
|
||||
n*word, *maxError, ms, algbw);
|
||||
}
|
||||
|
||||
for(int g=0; g<gpus; ++g) {
|
||||
CUDACHECK(cudaSetDevice(list[g]));
|
||||
CUDACHECK(cudaStreamDestroy(stream[g]));
|
||||
ncclCommDestroy(comm[g]);
|
||||
CUDACHECK(cudaFree(input[g]));
|
||||
if(g == 0) {
|
||||
CUDACHECK(cudaFree(output));
|
||||
CUDACHECK(cudaFreeHost(maxError));
|
||||
}
|
||||
}
|
||||
|
||||
free(input);
|
||||
free(comm);
|
||||
free(stream);
|
||||
CUDACHECK(cudaFreeHost(refout));
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
@@ -1,249 +0,0 @@
|
||||
/*************************************************************************
|
||||
* Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of NVIDIA CORPORATION nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
||||
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
************************************************************************/
|
||||
|
||||
#include <chrono>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <string>
|
||||
#include <float.h>
|
||||
|
||||
#include "nccl.h"
|
||||
#include "test_utilities.h"
|
||||
#include <nvToolsExt.h>
|
||||
|
||||
void showUsage(const char* bin) {
|
||||
printf("\n"
|
||||
"Usage: %s <type> <op> <n_min> <n_max> [delta] [gpus] [gpu0 [gpu1 [...]]]\n"
|
||||
"Where:\n"
|
||||
#ifdef CUDA_HAS_HALF
|
||||
" type = [char|int|half|float|double|int64|uint64]\n"
|
||||
#else
|
||||
" type = [char|int|float|double|int64|uint64]\n"
|
||||
#endif
|
||||
" op = [sum|prod|max|min]\n"
|
||||
" n_min > 0\n"
|
||||
" n_max >= n_min\n"
|
||||
" delta > 0\n\n", bin);
|
||||
return;
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
int nvis = 0;
|
||||
CUDACHECK(cudaGetDeviceCount(&nvis));
|
||||
if (nvis == 0) {
|
||||
printf("No GPUs found\n");
|
||||
showUsage(argv[0]);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
ncclDataType_t type;
|
||||
ncclRedOp_t op;
|
||||
int n_min;
|
||||
int n_max;
|
||||
int delta;
|
||||
int gpus;
|
||||
int* list = NULL;
|
||||
|
||||
if (argc < 5) {
|
||||
showUsage(argv[0]);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
type = strToType(argv[1]);
|
||||
if (type == nccl_NUM_TYPES) {
|
||||
printf("Invalid <type> '%s'\n", argv[1]);
|
||||
showUsage(argv[0]);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
op = strToOp(argv[2]);
|
||||
if (op == nccl_NUM_OPS) {
|
||||
printf("Invalid <op> '%s'\n", argv[2]);
|
||||
showUsage(argv[0]);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
n_min = strToPosInt(argv[3]);
|
||||
if (n_min < 1) {
|
||||
printf("Invalid <n_min> '%s'\n", argv[3]);
|
||||
showUsage(argv[0]);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
n_max = strToPosInt(argv[4]);
|
||||
if (n_max < n_min) {
|
||||
printf("Invalid <n_max> '%s'\n", argv[4]);
|
||||
showUsage(argv[0]);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (argc > 5) {
|
||||
delta = strToPosInt(argv[5]);
|
||||
if (delta < 1) {
|
||||
printf("Invalid <delta> '%s'\n", argv[5]);
|
||||
showUsage(argv[0]);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
} else {
|
||||
delta = (n_max == n_min) ? 1 : (n_max - n_min+9) / 10;
|
||||
}
|
||||
|
||||
if (argc > 6) {
|
||||
gpus = strToPosInt(argv[6]);
|
||||
if (gpus < 1) {
|
||||
printf("Invalid <gpus> '%s'\n", argv[6]);
|
||||
showUsage(argv[0]);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
} else {
|
||||
gpus = nvis;
|
||||
}
|
||||
|
||||
list = (int*)malloc(gpus*sizeof(int));
|
||||
|
||||
if (argc > 7 && argc != 7+gpus) {
|
||||
printf("If given, GPU list must be fully specified.\n");
|
||||
showUsage(argv[0]);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
for(int g=0; g<gpus; ++g) {
|
||||
if(argc > 7) {
|
||||
list[g] = strToNonNeg(argv[7+g]);
|
||||
if (list[g] < 0) {
|
||||
printf("Invalid GPU%d '%s'\n", g, argv[7+g]);
|
||||
showUsage(argv[0]);
|
||||
exit(EXIT_FAILURE);
|
||||
} else if (list[g] >= nvis) {
|
||||
printf("GPU%d (%d) exceeds visible devices (%d)\n", g, list[g], nvis);
|
||||
showUsage(argv[0]);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
} else {
|
||||
list[g] = g % nvis;
|
||||
}
|
||||
}
|
||||
|
||||
size_t word = wordSize(type);
|
||||
size_t max_output = n_max * word;
|
||||
size_t max_input = gpus * max_output;
|
||||
void* refout;
|
||||
CUDACHECK(cudaMallocHost(&refout, max_input)); // contains entire reduction
|
||||
|
||||
void **input, **output;
|
||||
double** localError;
|
||||
ncclComm_t* comm;
|
||||
cudaStream_t* stream;
|
||||
|
||||
input = (void**)malloc(gpus*sizeof(void*));
|
||||
output = (void**)malloc(gpus*sizeof(void*));
|
||||
localError = (double**)malloc(gpus*sizeof(double*));
|
||||
comm = (ncclComm_t*)malloc(gpus*sizeof(ncclComm_t));
|
||||
stream = (cudaStream_t*)malloc(gpus*sizeof(cudaStream_t));
|
||||
|
||||
for(int g=0; g<gpus; ++g) {
|
||||
char busid[32] = {0};
|
||||
CUDACHECK(cudaDeviceGetPCIBusId(busid, 32, list[g]));
|
||||
printf("# Rank %d using device %d [%s]\n", g, list[g], busid);
|
||||
|
||||
CUDACHECK(cudaSetDevice(list[g]));
|
||||
CUDACHECK(cudaMalloc(&input[g], max_input));
|
||||
CUDACHECK(cudaMalloc(&output[g], max_output));
|
||||
CUDACHECK(cudaMallocHost(&localError[g], sizeof(double)));
|
||||
CUDACHECK(cudaStreamCreate(&stream[g]));
|
||||
makeRandom(input[g], n_max*gpus, type, 42+g);
|
||||
|
||||
if (g == 0)
|
||||
CUDACHECK(cudaMemcpy(refout, input[g], max_input, cudaMemcpyDeviceToHost));
|
||||
else
|
||||
accVec(refout, input[g], n_max*gpus, type, op);
|
||||
}
|
||||
|
||||
NCCLCHECK(ncclCommInitAll(comm, gpus, list));
|
||||
|
||||
printf(" BYTES ERROR MSEC ALGBW BUSBW\n");
|
||||
|
||||
for(int n=n_min; n<=n_max; n+=delta) {
|
||||
size_t bytes = word * n;
|
||||
|
||||
for(int g=0; g<gpus; ++g) {
|
||||
CUDACHECK(cudaSetDevice(list[g]));
|
||||
CUDACHECK(cudaMemsetAsync(output[g], 0, bytes, stream[g]));
|
||||
CUDACHECK(cudaStreamSynchronize(stream[g]));
|
||||
}
|
||||
|
||||
auto start = std::chrono::high_resolution_clock::now();
|
||||
for(int g=0; g<gpus; ++g) {
|
||||
CUDACHECK(cudaSetDevice(list[g]));
|
||||
NCCLCHECK(ncclReduceScatter(input[g], output[g], n, type, op, comm[g], stream[g]));
|
||||
}
|
||||
for(int g=0; g<gpus; ++g) {
|
||||
CUDACHECK(cudaSetDevice(list[g]));
|
||||
CUDACHECK(cudaStreamSynchronize(stream[g]));
|
||||
}
|
||||
auto stop = std::chrono::high_resolution_clock::now();
|
||||
double ms = std::chrono::duration_cast<std::chrono::duration<double>>
|
||||
(stop - start).count() * 1000.0;
|
||||
|
||||
double max_error = 0.0;
|
||||
for(int g=0; g<gpus; ++g) {
|
||||
CUDACHECK(cudaSetDevice(list[g]));
|
||||
void* myRef = (void*)((char*)refout + g*bytes);
|
||||
maxDiff(localError[g], output[g], myRef, n, type, stream[g]);
|
||||
}
|
||||
for(int g=0; g<gpus; ++g) {
|
||||
CUDACHECK(cudaSetDevice(list[g]));
|
||||
CUDACHECK(cudaStreamSynchronize(stream[g]));
|
||||
max_error = max(max_error, *localError[g]);
|
||||
}
|
||||
|
||||
double mb = (double)bytes * 1.e-6;
|
||||
double algbw = mb / ms;
|
||||
double busbw = algbw * (double)(gpus - 1);
|
||||
printf("%12lu %5.0le %10.3lf %6.2lf %6.2lf\n",
|
||||
n*word, max_error, ms, algbw, busbw);
|
||||
}
|
||||
|
||||
for(int g=0; g<gpus; ++g) {
|
||||
CUDACHECK(cudaSetDevice(list[g]));
|
||||
CUDACHECK(cudaStreamDestroy(stream[g]));
|
||||
ncclCommDestroy(comm[g]);
|
||||
CUDACHECK(cudaFree(input[g]));
|
||||
CUDACHECK(cudaFree(output[g]));
|
||||
CUDACHECK(cudaFreeHost(localError[g]));
|
||||
}
|
||||
|
||||
free(localError);
|
||||
free(output);
|
||||
free(input);
|
||||
free(comm);
|
||||
free(stream);
|
||||
CUDACHECK(cudaFreeHost(refout));
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
@@ -1,285 +0,0 @@
|
||||
/*************************************************************************
|
||||
* Copyright (c) 2015-2016, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* See LICENCE.txt for license information
|
||||
************************************************************************/
|
||||
|
||||
#include <chrono>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "nccl.h"
|
||||
#include "test_utilities.h"
|
||||
|
||||
int errors = 0;
|
||||
double avg_bw = 0.0;
|
||||
int avg_count = 0;
|
||||
bool is_reduction = true;
|
||||
|
||||
template<typename T>
|
||||
void RunTest(T** sendbuff, T** recvbuff, const int N, const ncclDataType_t type,
|
||||
const ncclRedOp_t op, ncclComm_t* const comms, const std::vector<int>& dList) {
|
||||
// initialize data
|
||||
int nDev = 0;
|
||||
NCCLCHECK(ncclCommCount(comms[0], &nDev));
|
||||
cudaStream_t* s = (cudaStream_t*)malloc(sizeof(cudaStream_t)*nDev);
|
||||
|
||||
T* buffer = (T*)malloc(N * nDev * sizeof(T));
|
||||
T* result = (T*)malloc(N * nDev * sizeof(T));
|
||||
memset(buffer, 0, N * nDev * sizeof(T));
|
||||
memset(result, 0, N * nDev * sizeof(T));
|
||||
|
||||
for (int i = 0; i < nDev; ++i) {
|
||||
CUDACHECK(cudaSetDevice(dList[i]));
|
||||
CUDACHECK(cudaStreamCreate(s+i));
|
||||
CUDACHECK(cudaMemset(recvbuff[i], 0, N * sizeof(T)));
|
||||
Randomize(sendbuff[i], N * nDev, i);
|
||||
|
||||
if (i == 0) {
|
||||
CUDACHECK(cudaMemcpy(result, sendbuff[i], N * nDev * sizeof(T),
|
||||
cudaMemcpyDeviceToHost));
|
||||
} else {
|
||||
Accumulate<T>(result, sendbuff[i], N * nDev, op);
|
||||
}
|
||||
}
|
||||
|
||||
// warm up GPU
|
||||
for (int i = 0; i < nDev; ++i) {
|
||||
CUDACHECK(cudaSetDevice(dList[i]));
|
||||
NCCLCHECK(ncclReduceScatter((const void*)sendbuff[i], (void*)recvbuff[i],
|
||||
std::min(N, 1024 * 1024), type, op, comms[i], s[i]));
|
||||
}
|
||||
|
||||
for (int i = 0; i < nDev; ++i) {
|
||||
CUDACHECK(cudaSetDevice(dList[i]));
|
||||
CUDACHECK(cudaStreamSynchronize(s[i]));
|
||||
}
|
||||
|
||||
// for (int n = 0; n <= N; n = (n > 0) ? n << 1 : 1)
|
||||
{
|
||||
int n = N;
|
||||
printf("%12i %12i %6s %6s", (int)(n * sizeof(T)), n,
|
||||
TypeName(type).c_str(), OperationName(op).c_str());
|
||||
|
||||
// do out-of-place reduction first
|
||||
auto start = std::chrono::high_resolution_clock::now();
|
||||
|
||||
for (int i = 0; i < nDev; ++i) {
|
||||
CUDACHECK(cudaSetDevice(dList[i]));
|
||||
NCCLCHECK(ncclReduceScatter((const void*)sendbuff[i], (void*)recvbuff[i], n, type,
|
||||
op, comms[i], s[i]));
|
||||
}
|
||||
|
||||
for (int i = 0; i < nDev; ++i) {
|
||||
CUDACHECK(cudaSetDevice(dList[i]));
|
||||
CUDACHECK(cudaStreamSynchronize(s[i]));
|
||||
}
|
||||
|
||||
auto stop = std::chrono::high_resolution_clock::now();
|
||||
|
||||
double elapsedSec =
|
||||
std::chrono::duration_cast<std::chrono::duration<double>>(
|
||||
stop - start).count();
|
||||
double algbw = (double)(n * sizeof(T)) / 1.0E9 / elapsedSec;
|
||||
double busbw = algbw * (double)(nDev - 1);
|
||||
|
||||
double maxDelta = 0.0;
|
||||
for (int i = 0; i < nDev; ++i) {
|
||||
CUDACHECK(cudaSetDevice(dList[i]));
|
||||
double tmpDelta = CheckDelta<T>(recvbuff[i], result+i*n, n);
|
||||
maxDelta = std::max(tmpDelta, maxDelta);
|
||||
}
|
||||
|
||||
printf(" %7.3f %5.2f %5.2f %7.0le", elapsedSec * 1.0E3, algbw, busbw,
|
||||
maxDelta);
|
||||
|
||||
if (maxDelta > deltaMaxValue(type, is_reduction)) errors++;
|
||||
avg_bw += busbw;
|
||||
avg_count++;
|
||||
}
|
||||
|
||||
{
|
||||
// now do in-place reduction
|
||||
int n = N;
|
||||
|
||||
auto start = std::chrono::high_resolution_clock::now();
|
||||
|
||||
for (int i = 0; i < nDev; ++i) {
|
||||
CUDACHECK(cudaSetDevice(dList[i]));
|
||||
NCCLCHECK(ncclReduceScatter((const void*)sendbuff[i], (void*)sendbuff[i], n, type,
|
||||
op, comms[i], s[i]));
|
||||
}
|
||||
|
||||
for (int i = 0; i < nDev; ++i) {
|
||||
CUDACHECK(cudaSetDevice(dList[i]));
|
||||
CUDACHECK(cudaStreamSynchronize(s[i]));
|
||||
}
|
||||
|
||||
auto stop = std::chrono::high_resolution_clock::now();
|
||||
|
||||
double elapsedSec =
|
||||
std::chrono::duration_cast<std::chrono::duration<double>>(
|
||||
stop - start).count();
|
||||
double algbw = (double)(n * sizeof(T)) / 1.0E9 / elapsedSec;
|
||||
double busbw = algbw * (double)(nDev - 1);
|
||||
|
||||
double maxDelta = 0.0;
|
||||
for (int i = 0; i < nDev; ++i) {
|
||||
CUDACHECK(cudaSetDevice(dList[i]));
|
||||
double tmpDelta = CheckDelta<T>(sendbuff[i], result+i*n, n);
|
||||
maxDelta = std::max(tmpDelta, maxDelta);
|
||||
}
|
||||
|
||||
printf(" %7.3f %5.2f %5.2f %7.0le\n", elapsedSec * 1.0E3, algbw, busbw,
|
||||
maxDelta);
|
||||
|
||||
if (maxDelta > deltaMaxValue(type, is_reduction)) errors++;
|
||||
avg_bw += busbw;
|
||||
avg_count++;
|
||||
}
|
||||
|
||||
for (int i = 0; i < nDev; ++i) {
|
||||
CUDACHECK(cudaSetDevice(dList[i]));
|
||||
CUDACHECK(cudaStreamDestroy(s[i]));
|
||||
}
|
||||
free(s);
|
||||
free(buffer);
|
||||
free(result);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void RunTests(const int N, const ncclDataType_t type, ncclComm_t* const comms,
|
||||
const std::vector<int>& dList) {
|
||||
int nDev = 0;
|
||||
NCCLCHECK(ncclCommCount(comms[0], &nDev));
|
||||
T** sendbuff = (T**)malloc(nDev * sizeof(T*));
|
||||
T** recvbuff = (T**)malloc(nDev * sizeof(T*));
|
||||
|
||||
for (int i = 0; i < nDev; ++i) {
|
||||
CUDACHECK(cudaSetDevice(dList[i]));
|
||||
CUDACHECK(cudaMalloc(sendbuff + i, N * nDev * sizeof(T)));
|
||||
CUDACHECK(cudaMalloc(recvbuff + i, N * sizeof(T)));
|
||||
}
|
||||
|
||||
for (ncclRedOp_t op : { ncclSum, ncclProd, ncclMax, ncclMin }) {
|
||||
// for (ncclRedOp_t op : { ncclSum }) {
|
||||
RunTest<T>(sendbuff, recvbuff, N, type, op, comms, dList);
|
||||
}
|
||||
|
||||
for (int i = 0; i < nDev; ++i) {
|
||||
CUDACHECK(cudaSetDevice(dList[i]));
|
||||
CUDACHECK(cudaFree(sendbuff[i]));
|
||||
CUDACHECK(cudaFree(recvbuff[i]));
|
||||
}
|
||||
|
||||
free(sendbuff);
|
||||
free(recvbuff);
|
||||
}
|
||||
|
||||
void usage() {
|
||||
printf("Tests nccl ReduceScatter with user supplied arguments.\n"
|
||||
" Usage: all_reduce_test <data size in bytes> [number of GPUs] "
|
||||
"[GPU 0] [GPU 1] ...\n\n");
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
int nVis = 0;
|
||||
CUDACHECK(cudaGetDeviceCount(&nVis));
|
||||
|
||||
int N = 0;
|
||||
if (argc > 1) {
|
||||
int t = sscanf(argv[1], "%d", &N);
|
||||
if (t == 0) {
|
||||
printf("Error: %s is not an integer!\n\n", argv[1]);
|
||||
usage();
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
} else {
|
||||
printf("Error: must specify at least data size in bytes!\n\n");
|
||||
usage();
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
int nDev = nVis;
|
||||
if (argc > 2) {
|
||||
int t = sscanf(argv[2], "%d", &nDev);
|
||||
if (t == 0) {
|
||||
printf("Error: %s is not an integer!\n\n", argv[1]);
|
||||
usage();
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
std::vector<int> dList(nDev);
|
||||
for (int i = 0; i < nDev; ++i)
|
||||
dList[i] = i % nVis;
|
||||
|
||||
if (argc > 3) {
|
||||
if (argc - 3 != nDev) {
|
||||
printf("Error: insufficient number of GPUs in list\n\n");
|
||||
usage();
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
for (int i = 0; i < nDev; ++i) {
|
||||
int t = sscanf(argv[3 + i], "%d", dList.data() + i);
|
||||
if (t == 0) {
|
||||
printf("Error: %s is not an integer!\n\n", argv[2 + i]);
|
||||
usage();
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ncclComm_t* comms = (ncclComm_t*)malloc(sizeof(ncclComm_t)*nDev);
|
||||
NCCLCHECK(ncclCommInitAll(comms, nDev, dList.data()));
|
||||
|
||||
printf("# Using devices\n");
|
||||
for (int g = 0; g < nDev; ++g) {
|
||||
int cudaDev;
|
||||
int rank;
|
||||
cudaDeviceProp prop;
|
||||
NCCLCHECK(ncclCommCuDevice(comms[g], &cudaDev));
|
||||
NCCLCHECK(ncclCommUserRank(comms[g], &rank));
|
||||
CUDACHECK(cudaGetDeviceProperties(&prop, cudaDev));
|
||||
printf("# Rank %2d uses device %2d [0x%02x] %s\n", rank, cudaDev,
|
||||
prop.pciBusID, prop.name);
|
||||
}
|
||||
printf("\n");
|
||||
|
||||
printf("# %10s %12s %6s %6s out-of-place "
|
||||
"in-place\n", "", "", "", "");
|
||||
printf("# %10s %12s %6s %6s %7s %5s %5s %7s %7s %5s %5s %7s\n",
|
||||
"bytes", "N", "type", "op", "time", "algbw", "busbw", "delta", "time",
|
||||
"algbw", "busbw", "delta");
|
||||
|
||||
RunTests<char>(N / sizeof(char), ncclChar, comms, dList);
|
||||
RunTests<int>(N / sizeof(int), ncclInt, comms, dList);
|
||||
#ifdef CUDA_HAS_HALF
|
||||
RunTests<half>(N / sizeof(half), ncclHalf, comms, dList);
|
||||
#endif
|
||||
RunTests<float>(N / sizeof(float), ncclFloat, comms, dList);
|
||||
RunTests<double>(N / sizeof(double), ncclDouble, comms, dList);
|
||||
RunTests<long long>(N / sizeof(long long), ncclInt64, comms, dList);
|
||||
RunTests<unsigned long long>(N / sizeof(unsigned long long), ncclUint64, comms, dList);
|
||||
|
||||
printf("\n");
|
||||
|
||||
for(int i=0; i<nDev; ++i)
|
||||
ncclCommDestroy(comms[i]);
|
||||
free(comms);
|
||||
|
||||
char* str = getenv("NCCL_TESTS_MIN_BW");
|
||||
double check_avg_bw = str ? atof(str) : -1;
|
||||
avg_bw /= avg_count;
|
||||
|
||||
printf(" Out of bounds values : %d %s\n", errors, errors ? "FAILED" : "OK");
|
||||
printf(" Avg bus bandwidth : %g %s\n", avg_bw, check_avg_bw == -1 ? "" : (avg_bw < check_avg_bw ? "FAILED" : "OK"));
|
||||
printf("\n");
|
||||
if (errors || avg_bw < check_avg_bw)
|
||||
exit(EXIT_FAILURE);
|
||||
else
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
@@ -1,299 +0,0 @@
|
||||
/*************************************************************************
|
||||
* Copyright (c) 2015-2016, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* See LICENCE.txt for license information
|
||||
************************************************************************/
|
||||
|
||||
#include <chrono>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "nccl.h"
|
||||
#include "test_utilities.h"
|
||||
#include <nvToolsExt.h>
|
||||
|
||||
int csv = false;
|
||||
int errors = 0;
|
||||
double avg_bw = 0.0;
|
||||
int avg_count = 0;
|
||||
bool is_reduction = true;
|
||||
|
||||
template<typename T>
|
||||
void RunTest(T** sendbuff, T** recvbuff, const int N, const ncclDataType_t type,
|
||||
const ncclRedOp_t op, int root, ncclComm_t* const comms,
|
||||
const std::vector<int>& dList) {
|
||||
|
||||
// initialize data
|
||||
T* buffer = (T*)malloc(N * sizeof(T));
|
||||
T* result = (T*)malloc(N * sizeof(T));
|
||||
memset(buffer, 0, N * sizeof(T));
|
||||
memset(result, 0, N * sizeof(T));
|
||||
|
||||
int nDev = 0;
|
||||
NCCLCHECK(ncclCommCount(comms[0], &nDev));
|
||||
cudaStream_t* s = (cudaStream_t*)malloc(sizeof(cudaStream_t)*nDev);
|
||||
|
||||
for (int i = 0; i < nDev; ++i) {
|
||||
CUDACHECK(cudaSetDevice(dList[i]));
|
||||
CUDACHECK(cudaStreamCreate(s+i));
|
||||
CUDACHECK(cudaMemset(recvbuff[i], 0, N * sizeof(T)));
|
||||
Randomize(sendbuff[i], N, i);
|
||||
if(i == 0) {
|
||||
CUDACHECK(cudaMemcpy(result, sendbuff[i], N*sizeof(T), cudaMemcpyDeviceToHost));
|
||||
} else {
|
||||
Accumulate<T>(result, sendbuff[i], N, op);
|
||||
}
|
||||
}
|
||||
|
||||
// warm up GPU
|
||||
for (int i = 0; i < nDev; ++i) {
|
||||
CUDACHECK(cudaSetDevice(dList[i]));
|
||||
NCCLCHECK(ncclReduce((const void*)sendbuff[i], (void*)recvbuff[i], std::min(N, 1024 * 1024),
|
||||
type, op, root, comms[i], s[i]));
|
||||
}
|
||||
|
||||
for (int i = 0; i < nDev; ++i) {
|
||||
CUDACHECK(cudaSetDevice(dList[i]));
|
||||
CUDACHECK(cudaStreamSynchronize(s[i]));
|
||||
}
|
||||
|
||||
// for (int n = 0; n <= N; n = (n > 0) ? n << 1 : 1)
|
||||
{
|
||||
int n = N;
|
||||
printf((csv) ? "%i,%i,%s,%s,%d," : "%12i %12i %6s %6s %4d",
|
||||
(int) (n * sizeof(T)), n, TypeName(type).c_str(),
|
||||
OperationName(op).c_str(), root);
|
||||
|
||||
// do out-of-place reduction first
|
||||
nvtxRangePushA("out of place");
|
||||
auto start = std::chrono::high_resolution_clock::now();
|
||||
//for (int i=0; i<100; i++) {
|
||||
for (int i = 0; i < nDev; ++i) {
|
||||
CUDACHECK(cudaSetDevice(dList[i]));
|
||||
NCCLCHECK(ncclReduce((const void*)sendbuff[i], (void*)recvbuff[i], n, type, op,
|
||||
root, comms[i], s[i]));
|
||||
}
|
||||
//}
|
||||
|
||||
for (int i = 0; i < nDev; ++i) {
|
||||
CUDACHECK(cudaSetDevice(dList[i]));
|
||||
CUDACHECK(cudaStreamSynchronize(s[i]));
|
||||
}
|
||||
|
||||
auto stop = std::chrono::high_resolution_clock::now();
|
||||
nvtxRangePop();
|
||||
|
||||
nvtxRangePushA("out of place bookkeeping");
|
||||
double elapsedSec =
|
||||
std::chrono::duration_cast<std::chrono::duration<double>>(
|
||||
stop - start).count(); // / 100.0;
|
||||
double algbw = (double)(n * sizeof(T)) / 1.0E9 / elapsedSec;
|
||||
double busbw = algbw;
|
||||
|
||||
CUDACHECK(cudaSetDevice(dList[root]));
|
||||
double maxDelta = CheckDelta<T>(recvbuff[root], result, N);
|
||||
|
||||
printf((csv)?"%f,%f,%f,%le,":" %7.3f %5.2f %5.2f %7.0le",
|
||||
elapsedSec * 1.0E3, algbw, busbw, maxDelta);
|
||||
|
||||
if (maxDelta > deltaMaxValue(type, is_reduction)) errors++;
|
||||
avg_bw += busbw;
|
||||
avg_count++;
|
||||
|
||||
nvtxRangePop();
|
||||
}
|
||||
|
||||
|
||||
// for (int n = 0; n <= N; n = (n > 0) ? n << 1 : 1)
|
||||
{
|
||||
int n = N;
|
||||
// now do in-place reduction
|
||||
nvtxRangePushA("in place");
|
||||
auto start = std::chrono::high_resolution_clock::now();
|
||||
//for (int i=0; i<100; i++) {
|
||||
for (int i = 0; i < nDev; ++i) {
|
||||
CUDACHECK(cudaSetDevice(dList[i]));
|
||||
NCCLCHECK(ncclReduce((const void*)sendbuff[i], (void*)sendbuff[i], n, type, op,
|
||||
root, comms[i], s[i]));
|
||||
}
|
||||
//}
|
||||
|
||||
for (int i = 0; i < nDev; ++i) {
|
||||
CUDACHECK(cudaSetDevice(dList[i]));
|
||||
CUDACHECK(cudaStreamSynchronize(s[i]));
|
||||
}
|
||||
|
||||
auto stop = std::chrono::high_resolution_clock::now();
|
||||
nvtxRangePop();
|
||||
|
||||
nvtxRangePushA("in place bookkeeping");
|
||||
double elapsedSec =
|
||||
std::chrono::duration_cast<std::chrono::duration<double>>(
|
||||
stop - start).count(); // / 100.0;
|
||||
double algbw = (double)(n * sizeof(T)) / 1.0E9 / elapsedSec;
|
||||
double busbw = algbw;
|
||||
|
||||
CUDACHECK(cudaSetDevice(dList[root]));
|
||||
double maxDelta = CheckDelta<T>(sendbuff[root], result, N);
|
||||
|
||||
printf((csv)?"%f,%f,%f,%le,":" %7.3f %5.2f %5.2f %7.0le\n",
|
||||
elapsedSec * 1.0E3, algbw, busbw, maxDelta);
|
||||
|
||||
if (maxDelta > deltaMaxValue(type, is_reduction)) errors++;
|
||||
avg_bw += busbw;
|
||||
avg_count++;
|
||||
|
||||
nvtxRangePop();
|
||||
}
|
||||
|
||||
for (int i = 0; i < nDev; ++i) {
|
||||
CUDACHECK(cudaSetDevice(dList[i]));
|
||||
CUDACHECK(cudaStreamDestroy(s[i]));
|
||||
}
|
||||
free(s);
|
||||
free(buffer);
|
||||
free(result);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void RunTests(const int N, const ncclDataType_t type, ncclComm_t* const comms,
|
||||
const std::vector<int>& dList) {
|
||||
int nDev = 0;
|
||||
NCCLCHECK(ncclCommCount(comms[0], &nDev));
|
||||
T** sendbuff = (T**)malloc(nDev * sizeof(T*));
|
||||
T** recvbuff = (T**)malloc(nDev * sizeof(T*));
|
||||
|
||||
for (int i = 0; i < nDev; ++i) {
|
||||
CUDACHECK(cudaSetDevice(dList[i]));
|
||||
CUDACHECK(cudaMalloc(sendbuff + i, N * sizeof(T)));
|
||||
CUDACHECK(cudaMalloc(recvbuff + i, N * sizeof(T)));
|
||||
}
|
||||
|
||||
for (ncclRedOp_t op : { ncclSum, ncclProd, ncclMax, ncclMin }) {
|
||||
// for (ncclRedOp_t op : { ncclSum }) {
|
||||
for(int root=0; root<nDev; ++root) {
|
||||
RunTest<T>(sendbuff, recvbuff, N, type, op, root, comms, dList);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < nDev; ++i) {
|
||||
CUDACHECK(cudaSetDevice(dList[i]));
|
||||
CUDACHECK(cudaFree(sendbuff[i]));
|
||||
CUDACHECK(cudaFree(recvbuff[i]));
|
||||
}
|
||||
|
||||
free(sendbuff);
|
||||
free(recvbuff);
|
||||
}
|
||||
|
||||
void usage() {
|
||||
printf("Tests nccl Reduce with user supplied arguments.\n"
|
||||
" Usage: reduce_test <data size in bytes> [number of GPUs] "
|
||||
"[GPU 0] [GPU 1] ...\n\n");
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
int nVis = 0;
|
||||
CUDACHECK(cudaGetDeviceCount(&nVis));
|
||||
|
||||
int N = 0;
|
||||
if (argc > 1) {
|
||||
int t = sscanf(argv[1], "%d", &N);
|
||||
if (t == 0) {
|
||||
printf("Error: %s is not an integer!\n\n", argv[1]);
|
||||
usage();
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
} else {
|
||||
printf("Error: must specify at least data size in bytes!\n\n");
|
||||
usage();
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
int nDev = nVis;
|
||||
if (argc > 2) {
|
||||
int t = sscanf(argv[2], "%d", &nDev);
|
||||
if (t == 0) {
|
||||
printf("Error: %s is not an integer!\n\n", argv[1]);
|
||||
usage();
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
std::vector<int> dList(nDev);
|
||||
for (int i = 0; i < nDev; ++i)
|
||||
dList[i] = i % nVis;
|
||||
|
||||
if (argc > 3) {
|
||||
if (argc - 3 != nDev) {
|
||||
printf("Error: insufficient number of GPUs in list\n\n");
|
||||
usage();
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
for (int i = 0; i < nDev; ++i) {
|
||||
int t = sscanf(argv[3 + i], "%d", dList.data() + i);
|
||||
if (t == 0) {
|
||||
printf("Error: %s is not an integer!\n\n", argv[2 + i]);
|
||||
usage();
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ncclComm_t* comms = (ncclComm_t*)malloc(sizeof(ncclComm_t)*nDev);
|
||||
NCCLCHECK(ncclCommInitAll(comms, nDev, dList.data()));
|
||||
|
||||
if (!csv) {
|
||||
printf("# Using devices\n");
|
||||
for (int g = 0; g < nDev; ++g) {
|
||||
int cudaDev;
|
||||
int rank;
|
||||
cudaDeviceProp prop;
|
||||
NCCLCHECK(ncclCommCuDevice(comms[g], &cudaDev));
|
||||
NCCLCHECK(ncclCommUserRank(comms[g], &rank));
|
||||
CUDACHECK(cudaGetDeviceProperties(&prop, cudaDev));
|
||||
printf("# Rank %2d uses device %2d [0x%02x] %s\n", rank, cudaDev,
|
||||
prop.pciBusID, prop.name);
|
||||
}
|
||||
printf("\n");
|
||||
|
||||
printf("# %10s %12s %6s %6s %4s out-of-place in-place\n", "", "", "", "", "");
|
||||
printf("# %10s %12s %6s %6s %4s %7s %5s %5s %7s %7s %5s %5s %7s\n",
|
||||
"bytes", "N", "type", "op", "root",
|
||||
"time", "algbw", "busbw", "res", "time", "algbw", "busbw", "res");
|
||||
}
|
||||
else {
|
||||
printf("B,N,type,op,root,oop_time,oop_algbw,oop_busbw,oop_res,ip_time,ip_algbw,ip_busbw,ip_res\n");
|
||||
}
|
||||
|
||||
RunTests<char>(N / sizeof(char), ncclChar, comms, dList);
|
||||
RunTests<int>(N / sizeof(int), ncclInt, comms, dList);
|
||||
#ifdef CUDA_HAS_HALF
|
||||
RunTests<half>(N / sizeof(half), ncclHalf, comms, dList);
|
||||
#endif
|
||||
RunTests<float>(N / sizeof(float), ncclFloat, comms, dList);
|
||||
RunTests<double>(N / sizeof(double), ncclDouble, comms, dList);
|
||||
RunTests<long long>(N / sizeof(long long), ncclInt64, comms, dList);
|
||||
RunTests<unsigned long long>(N / sizeof(unsigned long long), ncclUint64, comms, dList);
|
||||
|
||||
printf("\n");
|
||||
|
||||
for(int i = 0; i < nDev; ++i)
|
||||
ncclCommDestroy(comms[i]);
|
||||
free(comms);
|
||||
|
||||
char* str = getenv("NCCL_TESTS_MIN_BW");
|
||||
double check_avg_bw = str ? atof(str) : -1;
|
||||
avg_bw /= avg_count;
|
||||
|
||||
printf(" Out of bounds values : %d %s\n", errors, errors ? "FAILED" : "OK");
|
||||
printf(" Avg bus bandwidth : %g %s\n", avg_bw, check_avg_bw == -1 ? "" : (avg_bw < check_avg_bw ? "FAILED" : "OK"));
|
||||
printf("\n");
|
||||
if (errors || avg_bw < check_avg_bw)
|
||||
exit(EXIT_FAILURE);
|
||||
else
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user