2.22.3-1
Rework core for NVIDIA Trusted Computing
* Compress work structs so that they are shared between channels
* Utilize the full amount of kernel argument space permitted (4k)
before resorting to work fifo.
* Rework the task preprocessing phase.
* Use a separate abortDevFlag which is kept in sync with abortFlag
using cudaMemcpy operations.
* Rename src/include/align.h to src/include/bitops.h
Add lazy connection establishment for collective operations
* Move buffer allocation and connection establishment to the first
collective operation using that algorithm.
* Accelerate init time and reduce memory usage.
* Avoid allocating NVLS buffers if all calls are registered.
* Compute algo/proto in ncclLaunchCollTasksInfo early on.
* Connect peers in ncclCollPreconnectFunc if not connected already.
* Also move shared buffer creation to the first send/recv call.
Accelerate intra-node NVLink detection
* Make each rank only detect NVLinks attached to its GPU.
* Fuse XMLs to reconstruct the full NVLink topology
Add init profiling to report time spend in different init phases.
* Report timings of bootstrap, allgather, search, connect, etc.
* Add new "PROFILE" category for NCCL_DEBUG_SUBSYS.
Add support for PCI p2p on split PCI switches
* Detect split PCI switches through a kernel module exposing
switch information.
* Update the topology XML and graph to add those inter-switch
connections.
Add cost estimation API
* Add a new ncclGroupEndSimulate primitive to return the estimated
time a group would take.
Net/IB: Add separate traffic class for fifo messages
* Add NCCL_IB_FIFO_TC to control the traffic class of fifo messages
independently from NCCL_IB_TC.
Merges PR #1194
Net/IB: Add support for IB router
* Use flid instead of lid if subnets do not match
* Warn if flid is 0
Optimizations and fixes for device network offload (unpack)
* Double the default number of channels
* Cache netDeviceType
* Fix save/increment head logic to enable Tree support.
Support ncclGroupStart/End for ncclCommAbort/Destroy
* Allow Abort/Destroy to be called within a group when managing
multiple GPUs with a single process.
Improve Tuner API
* Provide to the plugin the original cost table so that the plugin
can leave unknown or disabled algo/proto combinations untouched.
* Remove nvlsSupport and collnetSupport.
Do not print version to stdout when using a debug file
* Also print version from all processes with INFO debug level.
Fixes issue #1271
Fix clang warnings in NVTX headers
* Update NVTX headers to the latest version
Fixes issue #1270
Disable port fusion in heterogeneous systems
* Do not fuse ports if a mix of multi-port and single port are detected.
Fix NVLS graphs search for dual NICs.
* Fix NVLS graph search when we have more than one NIC per GPU.
Fix crash with collnetDirect
* Add separate graph search for collnetDirect, testing alltoall paths
and working similarly to the NVLS search.
Fix hang when nodes have different CPU types
* Add the CPU type to the rank peer info.
* Align all ranks on the CPU type after the first allgather.
* Only use the aligned CPU type for all tuning operations.
Fixes issue #1136
Fixes issue #1184
Fix performance of registered send/recv operations
* Allow for single full size operations
* Add INFO to confirm the registration of send/recv buffers.
Move all sync ops to finalize stage
* Ensure ncclCommDestroy is non-blocking if ncclCommFinalize has
been called.
Improve error reporting during SHM segment creation
Improve support of various compilers
Merges PR #1177
Merges PR #1228
Allow net and tuner plugins to be statically linked
* Search for ncclNet or ncclTuner symbols in the main binary.
Merges PR #979
Plugin examples includes cleanup
* Harmonize err.h and common.h usage.
* Add mixed plugin with both net and tuner.
[ROCm/rccl commit: 178b6b7590]
This commit is contained in:
@@ -7,7 +7,7 @@
|
||||
#ifndef NCCL_COMM_H_
|
||||
#define NCCL_COMM_H_
|
||||
|
||||
#include "transport.h"
|
||||
//#include "transport.h"
|
||||
#include "p2p.h"
|
||||
#include "collectives.h"
|
||||
#include "nccl_tuner.h"
|
||||
@@ -15,6 +15,7 @@
|
||||
#include "strongstream.h"
|
||||
#include "nccl_net.h"
|
||||
#include "register.h"
|
||||
#include "graph.h"
|
||||
|
||||
#if CUDART_VERSION < 9000
|
||||
struct cudaLaunchParams {
|
||||
@@ -144,7 +145,7 @@ struct ncclChannel {
|
||||
struct ncclNvls nvls;
|
||||
|
||||
int id; // index of this channel
|
||||
uint32_t workFifoSent; // last used work index+1
|
||||
uint32_t workFifoProduced; // +1 successor of last used work fifo byte
|
||||
|
||||
/* comm split sharable resources */
|
||||
struct ncclChannelPeer* collnetPeers;
|
||||
@@ -153,22 +154,15 @@ struct ncclChannel {
|
||||
struct ncclDevChannelPeer* nvlsDevPeers;
|
||||
};
|
||||
|
||||
struct ncclWorkList {
|
||||
struct ncclWorkBatchList {
|
||||
struct ncclWorkBatchList* next;
|
||||
struct ncclDevWorkBatch batch;
|
||||
};
|
||||
struct alignas(16) ncclWorkList {
|
||||
struct ncclWorkList* next;
|
||||
struct ncclWork work;
|
||||
};
|
||||
|
||||
struct ncclPointerList {
|
||||
struct ncclPointerList* next;
|
||||
void *ptr;
|
||||
};
|
||||
|
||||
struct ncclNvlsMcHandleList {
|
||||
struct ncclNvlsMcHandleList *next;
|
||||
CUmemGenericAllocationHandle mcHandle;
|
||||
CUdeviceptr ptr;
|
||||
int dev;
|
||||
size_t size;
|
||||
enum ncclDevWorkType workType;
|
||||
int size; // Size of struct following this node
|
||||
// ncclDevWorkColl, ncclDevWorkColLReg, ncclDevWorkP2p[]...
|
||||
};
|
||||
|
||||
struct ncclCollnetHandleList {
|
||||
@@ -188,33 +182,190 @@ struct ncclKernelPlan {
|
||||
struct ncclKernelPlan* next;
|
||||
|
||||
bool persistent; // aka captured in a graph
|
||||
enum ncclDevWorkStorageType workStorageType;
|
||||
bool kernelSpecialized;
|
||||
void *kernelFn;
|
||||
int channelUbound; // only channels c < channelUbound are present
|
||||
int channelCount; // number of channels present
|
||||
uint64_t channelMask; // which channels are present, channelCount == popcount(channelMask)
|
||||
struct ncclDevKernelArgs* kernelArgs;
|
||||
size_t kernelArgsSize;
|
||||
uint64_t channelMask; // bitset of which channels are present
|
||||
bool hasProxyOps; // does any channel have a non-empty proxyOpQueue
|
||||
int threadPerBlock;
|
||||
// workHeap fields are null until uploadWorkFifo() or preparePersistentKernel()
|
||||
struct ncclWork* workHead;
|
||||
|
||||
int collOpCount; // zero based for this plan
|
||||
int collOpCount; // Number of collectives in this plan.
|
||||
int nWorkBatches; // Number of work batches.
|
||||
size_t workBytes; // Sum size of all work (in the fifo) in bytes.
|
||||
struct ncclIntruQueue<struct ncclWorkList, &ncclWorkList::next> workQueue;
|
||||
struct ncclIntruQueue<struct ncclCommCallback, &ncclCommCallback::next> cleanupQueue;
|
||||
void* workBufPersistent;
|
||||
|
||||
struct ncclIntruQueue<struct ncclPointerList, &ncclPointerList::next> ipcMemQueue;
|
||||
struct ncclIntruQueue<struct ncclNvlsMcHandleList, &ncclNvlsMcHandleList::next> nvlsMcHandleQueue;
|
||||
struct ncclIntruQueue<struct ncclCollnetHandleList, &ncclCollnetHandleList::next> collnetHandleQueue;
|
||||
struct ncclIntruQueue<struct ncclProxyOp, &ncclProxyOp::enqNext> proxyOpQueue;
|
||||
};
|
||||
|
||||
struct Channel {
|
||||
int nWork;
|
||||
union {
|
||||
int nWorkElem; // used for coll and reg coll
|
||||
int p2pTailElem[2]; // used for p2p, indexed by ncclWorkElemP2pType-1
|
||||
};
|
||||
size_t collBytes;
|
||||
struct ncclIntruQueue<struct ncclWorkList, &ncclWorkList::next> workQueue;
|
||||
struct ncclIntruQueue<struct ncclProxyOp, &ncclProxyOp::enqNext> proxyOpQueue;
|
||||
} channels[MAXCHANNELS];
|
||||
size_t maxBytesPerChannel;
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
struct ncclTaskColl {
|
||||
struct ncclTaskColl* next;
|
||||
ncclFunc_t func;
|
||||
void const* sendbuff;
|
||||
void* recvbuff;
|
||||
size_t count;
|
||||
int root;
|
||||
ncclDataType_t datatype;
|
||||
ncclRedOp_t opHost;
|
||||
struct ncclDevRedOpFull opDev;
|
||||
int chunkSteps, sliceSteps;
|
||||
// Computed later:
|
||||
size_t trafficBytes;
|
||||
int32_t nMaxChannels:8;
|
||||
int32_t nWarps:8;
|
||||
int32_t algorithm:8, protocol:8;
|
||||
uint32_t isCollnet:1, isNvls:1;
|
||||
uint32_t devFuncId:30;
|
||||
enum ncclRegBufferType regBufType;
|
||||
// number of elements in planner->ipcMemQueue associated with this collective
|
||||
int nCleanupQueueElts;
|
||||
|
||||
void* sendMhandle;
|
||||
void* recvMhandle;
|
||||
};
|
||||
struct ncclTaskP2p {
|
||||
struct ncclTaskP2p* next;
|
||||
void* buff;
|
||||
size_t bytes;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Roughly sorts ncclTaskColl's by their size descending. This structure is
|
||||
// self-referential, meaning that pointers it contains internally may point
|
||||
// into the structure itself. This means that it is NOT memcpy-moveable:
|
||||
|
||||
struct ncclTaskCollSorter {
|
||||
static constexpr int UnitLog2 = 10; // 1K
|
||||
static constexpr size_t UnitSize = 1<<UnitLog2;
|
||||
static constexpr int MaxLog2 = 30; // 1GB
|
||||
static constexpr size_t MaxSize = 1ull<<MaxLog2;
|
||||
// Number of bins between powers of 2. For 4 bins, the worst case out-of-order
|
||||
// relative magnitude is (5/4)-1 = 25%
|
||||
static constexpr int BitsPerPow2 = 2;
|
||||
static constexpr int BinsPerPow2 = 1<<BitsPerPow2;
|
||||
static constexpr int BinCount = 1 + (MaxLog2-UnitLog2)*BinsPerPow2;
|
||||
|
||||
struct ncclTaskColl* head;
|
||||
struct ncclTaskColl* tail;
|
||||
// Least bin such that it and all above are empty.
|
||||
int binEdge;
|
||||
// Pointer to the pointer to this bin's head node which is either the
|
||||
// previous node's `next` field or `head`.
|
||||
struct ncclTaskColl** bins[BinCount];
|
||||
};
|
||||
|
||||
inline void ncclTaskCollSorterInsert(
|
||||
struct ncclTaskCollSorter* me, struct ncclTaskColl* x, size_t size
|
||||
) {
|
||||
constexpr int UnitLog2 = ncclTaskCollSorter::UnitLog2;
|
||||
constexpr size_t MaxSize = ncclTaskCollSorter::MaxSize;
|
||||
constexpr int BitsPerPow2 = ncclTaskCollSorter::BitsPerPow2;
|
||||
constexpr int BinCount = ncclTaskCollSorter::BinCount;
|
||||
int bin = u32fpEncode(std::min(MaxSize, size)>>UnitLog2, BitsPerPow2);
|
||||
bin = BinCount-1 - bin; // descending bin
|
||||
|
||||
if (me->bins[bin] == nullptr) {
|
||||
if (me->binEdge <= bin) {
|
||||
me->binEdge = bin+1;
|
||||
me->bins[bin] = me->tail ? &me->tail->next : &me->head;
|
||||
me->tail = x;
|
||||
} else {
|
||||
// Find successor non-empty bin after this one.
|
||||
int succ = bin+1;
|
||||
while (me->bins[succ] == nullptr) succ++;
|
||||
// What was our successor's head's previous is now our head's previous.
|
||||
me->bins[bin] = me->bins[succ];
|
||||
// The first node we insert is our tail, so that becomes our successor's
|
||||
// head's new previous.
|
||||
me->bins[succ] = &x->next;
|
||||
}
|
||||
}
|
||||
// Push a new head for this bin.
|
||||
x->next = *me->bins[bin];
|
||||
*me->bins[bin] = x;
|
||||
}
|
||||
|
||||
inline bool ncclTaskCollSorterEmpty(struct ncclTaskCollSorter* me) {
|
||||
return me->head == nullptr;
|
||||
}
|
||||
|
||||
// Reset sorter and return sorted linked list of its coll tasks.
|
||||
inline struct ncclTaskColl* ncclTaskCollSorterDequeueAll(struct ncclTaskCollSorter* me) {
|
||||
struct ncclTaskColl* head = me->head;
|
||||
if (head != nullptr) memset(me, 0, sizeof(*me));
|
||||
return head;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
struct ncclCudaStreamList {
|
||||
struct ncclCudaStreamList *next;
|
||||
cudaStream_t stream;
|
||||
};
|
||||
|
||||
struct ncclKernelPlanner {
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// State for accumulating tasks between ncclGroupStart/End()
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
struct Peer {
|
||||
bool sendSeen, recvSeen;
|
||||
struct ncclIntruQueue<struct ncclTaskP2p, &ncclTaskP2p::next> sendQueue;
|
||||
struct ncclIntruQueue<struct ncclTaskP2p, &ncclTaskP2p::next> recvQueue;
|
||||
};
|
||||
struct ncclTaskCollSorter collSorter;
|
||||
struct Peer* peers/*[nRanks]*/;
|
||||
int nTasksColl, nTasksP2p;
|
||||
bool persistent;
|
||||
|
||||
// The list of user streams aggregated over all tasks present.
|
||||
struct ncclCudaStreamList* streams;
|
||||
// The most recent user stream. Ignored if streams==nullptr
|
||||
cudaStream_t streamRecent;
|
||||
// The graph capturing all user streams or invalid if none. Thus we restrict the
|
||||
// user that all streams must be captured in the same graph or not captured
|
||||
// at all. Technically we could probably relax this, but that would mean
|
||||
// collecting a different `ncclTasks` per graph and one for non-graph.
|
||||
struct ncclCudaGraph capturingGraph;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// Lists of tasks to be assembled into plans.
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
struct ncclIntruQueue<struct ncclTaskColl, &ncclTaskColl::next> collTaskQueue;
|
||||
struct ncclIntruQueue<struct ncclWorkList, &ncclWorkList::next> collWorkQueue;
|
||||
struct ncclIntruQueue<struct ncclCommCallback, &ncclCommCallback::next> collCleanupQueue;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// State for building current (Work-In-Progress) plan:
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
struct WipPlan {
|
||||
struct Channel {
|
||||
struct {
|
||||
int workBytes; // Sum size of work metadata referenced by this batch.
|
||||
int nP2ps; // Number of p2p works in this batch
|
||||
int p2pRounds[NCCL_MAX_DEV_WORK_P2P_PER_BATCH]; // which rounds are present in this batch.
|
||||
} wipBatch; // work-in-progress batch which will be next tail of workBatchQueue
|
||||
int nWorkBatchesP2p; // number of p2p batches for this channel.
|
||||
struct ncclIntruQueue<struct ncclWorkBatchList, &ncclWorkBatchList::next> workBatchQueue;
|
||||
struct ncclIntruQueue<struct ncclProxyOp, &ncclProxyOp::enqNext> proxyOpQueue;
|
||||
} channels[MAXCHANNELS];
|
||||
} wipPlan;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// State for launching built plans:
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// List of kernel plans built form tasks.
|
||||
struct ncclIntruQueue<struct ncclKernelPlan, &ncclKernelPlan::next> planQueue;
|
||||
// First of the unlaunched kernels in `planQueue`
|
||||
struct ncclKernelPlan* unlaunchedPlansHead;
|
||||
};
|
||||
|
||||
#define NCCL_MAGIC 0x0280028002800280 // Nickel atomic number is 28.
|
||||
@@ -233,12 +384,18 @@ struct ncclComm {
|
||||
struct ncclPeerInfo* peerInfo;
|
||||
struct ncclTopoSystem* topo;
|
||||
|
||||
int netPluginLoaded;
|
||||
ncclNet_t* ncclNet;
|
||||
ncclNetDeviceType netDeviceType;
|
||||
ncclCollNet_t* ncclCollNet;
|
||||
void* bootstrap;
|
||||
// Bitmasks for ncclTransportP2pSetup
|
||||
uint64_t* connectSend;
|
||||
uint64_t* connectRecv;
|
||||
struct ncclTopoGraph graphs[NCCL_NUM_ALGORITHMS];
|
||||
bool initAlgoChannels[NCCL_NUM_ALGORITHMS];
|
||||
bool runtimeConn; // if dynamic connection is supported
|
||||
int cuMemSupport;
|
||||
|
||||
uint64_t magic; // Magic number for all network communication. Not a security key -- only goal is to detect mismatches.
|
||||
|
||||
@@ -253,6 +410,9 @@ struct ncclComm {
|
||||
cpu_set_t cpuAffinity; // CPU affinity of the GPU
|
||||
int cudaArch; // matches __CUDA_ARCH__ of device
|
||||
|
||||
int cpuArch; // architecture - As defined in src/include/graph.h, e.g. x86/arm/ppc/mixed
|
||||
int cpuVendor; // vendor - As defined in src/include/graph.h
|
||||
|
||||
int node;
|
||||
int nNodes;
|
||||
int localRank;
|
||||
@@ -278,10 +438,11 @@ struct ncclComm {
|
||||
int nChannels; // connection nChannels
|
||||
int collChannels; // enqueue nChannels
|
||||
int nvlsChannels; // enqueue nChannels
|
||||
// all nvls heads stored to check if we can splitShare
|
||||
int nvlsHeads[MAXCHANNELS];
|
||||
// Channels (per peer) for p2p
|
||||
int p2pnChannels;
|
||||
int p2pnChannelsPerPeer;
|
||||
int p2pChannels[MAXCHANNELS];
|
||||
|
||||
// Should this comm allocate LL buffers for network P2P connections?
|
||||
bool allocP2pNetLLBuffers;
|
||||
@@ -303,23 +464,28 @@ struct ncclComm {
|
||||
ncclResult_t asyncResult;
|
||||
|
||||
// Flag to ask NCCL kernels to abort
|
||||
volatile uint32_t *abortFlag;
|
||||
volatile uint32_t *childAbortFlag;
|
||||
uint32_t *abortFlagRefCount;
|
||||
uint32_t* abortFlag;
|
||||
uint32_t* abortFlagDev;
|
||||
int* abortFlagRefCount;
|
||||
uint32_t* childAbortFlag;
|
||||
uint32_t* childAbortFlagDev;
|
||||
uint32_t destroyFlag;
|
||||
|
||||
// Device side of the communicator (for cudaFree's)
|
||||
struct ncclDevComm* devComm; // actually = &ncclDevCommAndChannels::comm
|
||||
|
||||
// Operation pool.
|
||||
int workFifoDepth; // size of workFifoHeap[], power of 2
|
||||
struct ncclWork* workFifoHeap;
|
||||
struct ncclWork* devWorkFifoHeap;
|
||||
void* workFifoHeapGdrHandle;
|
||||
uint32_t workArgsBytes; // max size of kernel args
|
||||
uint32_t workFifoBytes; // size of workFifoBuf, power of 2
|
||||
void* workFifoBuf;
|
||||
void* workFifoBufDev;
|
||||
void* workFifoBufGdrHandle;
|
||||
|
||||
// Work completion notificaion
|
||||
uint32_t* workFifoDone/*[MAXCHANNELS]*/; // in cudaHost memory
|
||||
uint32_t workFifoSent; // Monotonic (mod 1<<32) index of next unused fifo slot.
|
||||
uint32_t workFifoAckdMin; // Monotonic index of least unprocessed fifo slot over all channels.
|
||||
// Monotonic number of bytes (mod 1<<32) consumed per channel. In cudaHost memory.
|
||||
uint32_t* workFifoConsumed/*[MAXCHANNELS]*/;
|
||||
// Last observed value of: min(workFifoConsumed[c] for c < MAXCHANNELS)
|
||||
uint32_t workFifoConsumedLeast;
|
||||
// Monotonic number of bytes (mod 1<<32) sent to fifo.
|
||||
uint32_t workFifoProduced;
|
||||
|
||||
// Intra-process sync
|
||||
struct ncclComm* intraComm0; // leader of intra-process comms (self possible)
|
||||
@@ -337,7 +503,7 @@ struct ncclComm {
|
||||
// Whether this communicator uses collNet
|
||||
int collNetSupport;
|
||||
bool collNetRegSupport;
|
||||
uint8_t collNetSupportMatrix[4/*sum,prod,min,max*/][ncclNumTypes];
|
||||
uint8_t collNetSupportMatrix[4/*sum,prod,max,min*/][ncclNumTypes];
|
||||
int intraHighestTransportType;
|
||||
int* collNetHeads;
|
||||
int collNetHeadsNum;
|
||||
@@ -355,16 +521,16 @@ struct ncclComm {
|
||||
// pools backed by comm->memPermanent
|
||||
struct ncclMemoryPool memPool_ncclProxyOp;
|
||||
struct ncclMemoryPool memPool_ncclKernelPlan;
|
||||
struct ncclMemoryPool memPool_ncclPointerList;
|
||||
struct ncclMemoryPool memPool_ncclNvlsHandleList;
|
||||
struct ncclMemoryPool memPool_ncclCollnetHandleList;
|
||||
|
||||
// Next comm in this thread's active ncclGroup[Start|End](). Holds "0x1" when
|
||||
// this comm is not yet in a group.
|
||||
struct ncclComm* groupNext;
|
||||
// Subset of those in groupNext list. Holds 0x1 if not needing preconnect.
|
||||
struct ncclComm* preconnectNext;
|
||||
int persistentRefs; // number of persistent plan-lists capturing this comm
|
||||
struct ncclTasks tasks;
|
||||
struct P2pSchedulePair { int sendRank; int recvRank; } *p2pSchedule;
|
||||
|
||||
struct ncclKernelPlanner planner;
|
||||
|
||||
// user-created reduction ops
|
||||
int userRedOpCapacity, userRedOpFreeHead;
|
||||
@@ -373,11 +539,6 @@ struct ncclComm {
|
||||
// Queue of things for the main thread to do
|
||||
struct ncclIntruQueueMpsc<struct ncclCommCallback, &ncclCommCallback::next> callbackQueue;
|
||||
|
||||
// List of kernel plans built form tasks.
|
||||
struct ncclIntruQueue<struct ncclKernelPlan, &ncclKernelPlan::next> planQueue;
|
||||
// First of the unlaunched kernels in `planQueue`
|
||||
struct ncclKernelPlan* unlaunchedPlansHead;
|
||||
|
||||
ncclConfig_t config;
|
||||
// initState is to more conveniently reclaim resources when errors happen.
|
||||
ncclResult_t initState;
|
||||
@@ -389,6 +550,7 @@ struct ncclComm {
|
||||
struct ncclGroupJob *groupJob;
|
||||
|
||||
// Tuning plugin
|
||||
int tunerPluginLoaded;
|
||||
ncclTuner_t* tuner;
|
||||
void *tunerContext;
|
||||
// buffer registration cache
|
||||
|
||||
Reference in New Issue
Block a user