60240fec77
Add scalable init API
* Add new ncclCommInitRankScalable to allow for passing multiple
unique IDs to the init function.
* Spreads the load onto multiple bootstrap roots, allowing for
constant bootstrap time.
* Requires multiple ranks to create a unique ID, and the CPU-side
ID exchange code to call allgather[v] instead of broadcast.
Accelerate init bootstrap operations
* Reduce the number of calls to allgather.
* Allow roots to reply early to ranks when information is already
available.
* Add an option to use ncclNet instead of sockets to perform
bootstrap allgather operations.
Add PAT algorithms for Allgather and ReduceScatter
* Parallel Aggregated Trees, variation of Bruck algorithm.
* Logarithmic number of network steps for small sizes at scale.
* Only supports one rank per node at the moment.
Add support for registered buffers for intra-node communication.
* Allow registered user buffers to be accessed directly intra-node
* Avoids extra copies in algorithms which permit it, saving
memory bandwidth and helping with compute overlap.
Add profiler plugin API
* New plugin API for profiling
* Supports various levels of profiling, with a hierarchy.
Asynchronous graph allocation
* Make calls to cudaMalloc and cudaMemcpy during graph allocation
asynchronous.
* Significantly speeds up graph capture.
Use fatal IB asynchronous events to stop network operation
* Avoids many other error messages
* Only fatal errors are affected; potentially transient errors
(e.g. port down) do not cause an immediate stop.
Set P2P level to PXB on AMD CPUs when using more than 2 GPUs per node
* P2P would cause a significant performance degradation when using
many GPUs, and therefore many interleaved data flows.
* Disable P2P through the CPU when we have 3+ GPUs per node; keep it
enabled when we only have 2 GPUs.
Improve the init logs to report the real NCCL function.
* Make the log report ncclCommInitRank or ncclCommSplit, rather than
the generic ncclCommInitRankFunc.
Add a parameter to set the location of the user configuration file.
* Add NCCL_CONF_FILE environment variable to set where the user's
configuration file resides.
Increase default IB timeout
* Increase IB timeout value from 18 to 20.
* Should help avoid fatal errors on large RoCE systems.
Add new check for nvidia peermem
* On linux kernels 6.6+, /sys/kernel/mm/memory_peers is no longer
present; check for /sys/module/nvidia_peermem/version instead.
Fix old performance regression when mixing small and large operations.
* Improves distribution of work on channels.
Fix crash when NUMA IDs are equal to -1.
* Can happen when a NIC is a virtual NIC, or when linux doesn't
know which NUMA node a device is attached to
* Issue NVIDIA/nccl-tests#233
Fix tree graph search when NCCL_CROSS_NIC is set to 1.
* Would force NCCL to use the balanced_tree pattern, thereby
disabling LL128 on platforms with 1 GPU+1 NIC per PCI switch.
* Would also try to use alternate rings even though it was not
needed.
Compiler tweaks and fixes
* PR #1177
* PR #1228
Fix stack smash
* PR #1325
Fixes for multi-node NVLink + IB operation
Coverity fixes and comments.
[ROCm/rccl commit: 68b542363f]
289 regels
9.1 KiB
C++
289 regels
9.1 KiB
C++
/*************************************************************************
|
|
* Copyright (c) 2019-2022, NVIDIA CORPORATION. All rights reserved.
|
|
*
|
|
* See LICENSE.txt for license information
|
|
************************************************************************/
|
|
|
|
#ifndef NCCL_BITOPS_H_
|
|
#define NCCL_BITOPS_H_
|
|
|
|
#include <stdint.h>
|
|
|
|
#if !__NVCC__
|
|
#ifndef __host__
|
|
#define __host__
|
|
#endif
|
|
#ifndef __device__
|
|
#define __device__
|
|
#endif
|
|
#endif
|
|
|
|
#define DIVUP(x, y) \
|
|
(((x)+(y)-1)/(y))
|
|
|
|
#define ROUNDUP(x, y) \
|
|
(DIVUP((x), (y))*(y))
|
|
|
|
#define ALIGN_POWER(x, y) \
|
|
((x) > (y) ? ROUNDUP(x, y) : ((y)/((y)/(x))))
|
|
|
|
#define ALIGN_SIZE(size, align) \
|
|
size = ((size + (align) - 1) / (align)) * (align);
|
|
|
|
template<typename X, typename Y, typename Z = decltype(X()+Y())>
|
|
__host__ __device__ constexpr Z divUp(X x, Y y) {
|
|
return (x+y-1)/y;
|
|
}
|
|
|
|
template<typename X, typename Y, typename Z = decltype(X()+Y())>
|
|
__host__ __device__ constexpr Z roundUp(X x, Y y) {
|
|
return (x+y-1) - (x+y-1)%y;
|
|
}
|
|
template<typename X, typename Y, typename Z = decltype(X()+Y())>
|
|
__host__ __device__ constexpr Z roundDown(X x, Y y) {
|
|
return x - x%y;
|
|
}
|
|
|
|
// assumes second argument is a power of 2
|
|
template<typename X, typename Z = decltype(X()+int())>
|
|
__host__ __device__ constexpr Z alignUp(X x, int a) {
|
|
return (x + a-1) & Z(-a);
|
|
}
|
|
// assumes second argument is a power of 2
|
|
template<typename X, typename Z = decltype(X()+int())>
|
|
__host__ __device__ constexpr Z alignDown(X x, int a) {
|
|
return x & Z(-a);
|
|
}
|
|
|
|
template<typename Int>
|
|
inline __host__ __device__ int countOneBits(Int x) {
|
|
#if __CUDA_ARCH__
|
|
if (sizeof(Int) <= sizeof(unsigned int)) {
|
|
return __popc((unsigned int)x);
|
|
} else if (sizeof(Int) <= sizeof(unsigned long long)) {
|
|
return __popcll((unsigned long long)x);
|
|
} else {
|
|
static_assert(sizeof(Int) <= sizeof(unsigned long long), "Unsupported integer size.");
|
|
return -1;
|
|
}
|
|
#else
|
|
if (sizeof(Int) <= sizeof(unsigned int)) {
|
|
return __builtin_popcount((unsigned int)x);
|
|
} else if (sizeof(Int) <= sizeof(unsigned long)) {
|
|
return __builtin_popcountl((unsigned long)x);
|
|
} else if (sizeof(Int) <= sizeof(unsigned long long)) {
|
|
return __builtin_popcountll((unsigned long long)x);
|
|
} else {
|
|
static_assert(sizeof(Int) <= sizeof(unsigned long long), "Unsupported integer size.");
|
|
return -1;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
// Returns index of first one bit or returns -1 if mask is zero.
|
|
template<typename Int>
|
|
inline __host__ __device__ int firstOneBit(Int mask) {
|
|
int i;
|
|
#if __CUDA_ARCH__
|
|
if (sizeof(Int) <= sizeof(int)) {
|
|
i = __ffs((int)mask);
|
|
} else if (sizeof(Int) <= sizeof(long long)) {
|
|
i = __ffsll((long long)mask);
|
|
} else {
|
|
static_assert(sizeof(Int) <= sizeof(long long), "Unsupported integer size.");
|
|
}
|
|
#else
|
|
if (sizeof(Int) <= sizeof(int)) {
|
|
i = __builtin_ffs((int)mask);
|
|
} else if (sizeof(Int) <= sizeof(long)) {
|
|
i = __builtin_ffsl((long)mask);
|
|
} else if (sizeof(Int) <= sizeof(long long)) {
|
|
i = __builtin_ffsll((long long)mask);
|
|
} else {
|
|
static_assert(sizeof(Int) <= sizeof(long long), "Unsupported integer size.");
|
|
}
|
|
#endif
|
|
return i-1;
|
|
}
|
|
|
|
template<typename Int>
|
|
inline __host__ __device__ int popFirstOneBit(Int* mask) {
|
|
Int tmp = *mask;
|
|
*mask &= *mask-1;
|
|
return firstOneBit(tmp);
|
|
}
|
|
|
|
template<typename Int>
|
|
inline __host__ __device__ int log2Down(Int x) {
|
|
int w, n;
|
|
#if __CUDA_ARCH__
|
|
if (sizeof(Int) <= sizeof(int)) {
|
|
w = 8*sizeof(int);
|
|
n = __clz((int)x);
|
|
} else if (sizeof(Int) <= sizeof(long long)) {
|
|
w = 8*sizeof(long long);
|
|
n = __clzll((long long)x);
|
|
} else {
|
|
static_assert(sizeof(Int) <= sizeof(long long), "Unsupported integer size.");
|
|
}
|
|
#else
|
|
if (x == 0) {
|
|
return -1;
|
|
} else if (sizeof(Int) <= sizeof(unsigned int)) {
|
|
w = 8*sizeof(unsigned int);
|
|
n = __builtin_clz((unsigned int)x);
|
|
} else if (sizeof(Int) <= sizeof(unsigned long)) {
|
|
w = 8*sizeof(unsigned long);
|
|
n = __builtin_clzl((unsigned long)x);
|
|
} else if (sizeof(Int) <= sizeof(unsigned long long)) {
|
|
w = 8*sizeof(unsigned long long);
|
|
n = __builtin_clzll((unsigned long long)x);
|
|
} else {
|
|
static_assert(sizeof(Int) <= sizeof(unsigned long long), "Unsupported integer size.");
|
|
}
|
|
#endif
|
|
return (w-1)-n;
|
|
}
|
|
|
|
template<typename Int>
|
|
inline __host__ __device__ int log2Up(Int x) {
|
|
int w, n;
|
|
if (x != 0) x -= 1;
|
|
#if __CUDA_ARCH__
|
|
if (sizeof(Int) <= sizeof(int)) {
|
|
w = 8*sizeof(int);
|
|
n = __clz((int)x);
|
|
} else if (sizeof(Int) <= sizeof(long long)) {
|
|
w = 8*sizeof(long long);
|
|
n = __clzll((long long)x);
|
|
} else {
|
|
static_assert(sizeof(Int) <= sizeof(long long), "Unsupported integer size.");
|
|
}
|
|
#else
|
|
if (x == 0) {
|
|
return 0;
|
|
} else if (sizeof(Int) <= sizeof(unsigned int)) {
|
|
w = 8*sizeof(unsigned int);
|
|
n = __builtin_clz((unsigned int)x);
|
|
} else if (sizeof(Int) <= sizeof(unsigned long)) {
|
|
w = 8*sizeof(unsigned long);
|
|
n = __builtin_clzl((unsigned long)x);
|
|
} else if (sizeof(Int) <= sizeof(unsigned long long)) {
|
|
w = 8*sizeof(unsigned long long);
|
|
n = __builtin_clzll((unsigned long long)x);
|
|
} else {
|
|
static_assert(sizeof(Int) <= sizeof(unsigned long long), "Unsupported integer size.");
|
|
}
|
|
#endif
|
|
return w-n;
|
|
}
|
|
|
|
template<typename Int>
|
|
inline __host__ __device__ Int pow2Up(Int x) {
|
|
return Int(1)<<log2Up(x);
|
|
}
|
|
|
|
template<typename Int>
|
|
inline __host__ __device__ Int pow2Down(Int x) {
|
|
// True, log2Down can return -1, but we don't normally pass 0 as an argument...
|
|
// coverity[negative_shift]
|
|
return Int(1)<<log2Down(x);
|
|
}
|
|
|
|
template<typename UInt, int nSubBits>
|
|
inline __host__ UInt reverseSubBits(UInt x) {
|
|
if (nSubBits >= 16 && 8*sizeof(UInt) == nSubBits) {
|
|
switch (8*sizeof(UInt)) {
|
|
case 16: x = __builtin_bswap16(x); break;
|
|
case 32: x = __builtin_bswap32(x); break;
|
|
case 64: x = __builtin_bswap64(x); break;
|
|
default: static_assert(8*sizeof(UInt) <= 64, "Unsupported integer type.");
|
|
}
|
|
return reverseSubBits<UInt, 8>(x);
|
|
} else if (nSubBits == 1) {
|
|
return x;
|
|
} else {
|
|
UInt m = UInt(-1)/((UInt(1)<<(nSubBits/2))+1);
|
|
x = (x & m)<<(nSubBits/2) | (x & ~m)>>(nSubBits/2);
|
|
return reverseSubBits<UInt, nSubBits/2>(x);
|
|
}
|
|
}
|
|
|
|
template<typename T> struct ncclToUnsigned;
|
|
template<> struct ncclToUnsigned<char> { using type = unsigned char; };
|
|
template<> struct ncclToUnsigned<signed char> { using type = unsigned char; };
|
|
template<> struct ncclToUnsigned<unsigned char> { using type = unsigned char; };
|
|
template<> struct ncclToUnsigned<signed short> { using type = unsigned short; };
|
|
template<> struct ncclToUnsigned<unsigned short> { using type = unsigned short; };
|
|
template<> struct ncclToUnsigned<signed int> { using type = unsigned int; };
|
|
template<> struct ncclToUnsigned<unsigned int> { using type = unsigned int; };
|
|
template<> struct ncclToUnsigned<signed long> { using type = unsigned long; };
|
|
template<> struct ncclToUnsigned<unsigned long> { using type = unsigned long; };
|
|
template<> struct ncclToUnsigned<signed long long> { using type = unsigned long long; };
|
|
template<> struct ncclToUnsigned<unsigned long long> { using type = unsigned long long; };
|
|
|
|
// Reverse the bottom nBits bits of x. The top bits will be overwritten with 0's.
|
|
template<typename Int>
|
|
inline __host__ __device__ Int reverseBits(Int x, int nBits) {
|
|
using UInt = typename ncclToUnsigned<Int>::type;
|
|
union { UInt ux; Int sx; };
|
|
sx = x;
|
|
#if __CUDA_ARCH__
|
|
if (sizeof(Int) <= sizeof(unsigned int)) {
|
|
ux = __brev(ux);
|
|
} else if (sizeof(Int) <= sizeof(unsigned long long)) {
|
|
ux = __brevll(ux);
|
|
} else {
|
|
static_assert(sizeof(Int) <= sizeof(unsigned long long), "Unsupported integer type.");
|
|
}
|
|
#else
|
|
ux = reverseSubBits<UInt, 8*sizeof(UInt)>(ux);
|
|
#endif
|
|
ux = nBits==0 ? 0 : ux>>(8*sizeof(UInt)-nBits);
|
|
return sx;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// Custom 8 bit floating point format for approximating 32 bit uints. This format
|
|
// has nearly the full range of uint32_t except it only keeps the top 3 bits
|
|
// beneath the leading 1 bit and thus has a max value of 0xf0000000.
|
|
|
|
inline __host__ __device__ uint32_t u32fpEncode(uint32_t x, int bitsPerPow2) {
|
|
int log2x;
|
|
#if __CUDA_ARCH__
|
|
log2x = 31-__clz(x|1);
|
|
#else
|
|
log2x = 31-__builtin_clz(x|1);
|
|
#endif
|
|
uint32_t mantissa = x>>(log2x >= bitsPerPow2 ? log2x-bitsPerPow2 : 0) & ((1u<<bitsPerPow2)-1);
|
|
uint32_t exponent = log2x >= bitsPerPow2 ? log2x-(bitsPerPow2-1) : 0;
|
|
return exponent<<bitsPerPow2 | mantissa;
|
|
}
|
|
|
|
inline __host__ __device__ uint32_t u32fpDecode(uint32_t x, int bitsPerPow2) {
|
|
uint32_t exponent = x>>bitsPerPow2;
|
|
uint32_t mantissa = (x & ((1u<<bitsPerPow2)-1)) | (exponent!=0 ? 0x8 : 0);
|
|
if (exponent != 0) exponent -= 1;
|
|
return mantissa<<exponent;
|
|
}
|
|
|
|
constexpr uint32_t u32fp8MaxValue() { return 0xf0000000; }
|
|
|
|
inline __host__ __device__ uint8_t u32fp8Encode(uint32_t x) {
|
|
return u32fpEncode(x, 3);
|
|
}
|
|
inline __host__ __device__ uint32_t u32fp8Decode(uint8_t x) {
|
|
return u32fpDecode(x, 3);
|
|
}
|
|
|
|
inline __host__ __device__ uint64_t getHash(const char* string, int n) {
|
|
// Based on DJB2a, result = result * 33 ^ char
|
|
uint64_t result = 5381;
|
|
for (int c = 0; c < n; c++) {
|
|
result = ((result << 5) + result) ^ string[c];
|
|
}
|
|
return result;
|
|
}
|
|
|
|
#endif
|