Open tyohei opened 5 years ago
struct ncclColl {
union {
struct {
struct CollectiveArgs args;
uint16_t funcIndex;
uint16_t nextIndex;
uint8_t active;
};
int data[0x10];
};
};
struct ncclInfo {
ncclColl_t coll;
const char* opName;
// NCCL Coll Args
const void* sendbuff;
void* recvbuff;
size_t count;
ncclDataType_t datatype;
ncclRedOp_t op;
int root;
ncclComm_t comm;
cudaStream_t stream;
// Algorithm details
int chunkSteps;
int sliceSteps;
// Computed later
ncclPattern_t pattern;
size_t nBytes;
int nstepsPerLoop;
int nchunksPerLoop;
};
struct CollectiveArgs {
struct ncclDevComm* comm;
uint64_t opCount;
// local and remote input, output, and buffer
const void * ThisInput;
void * ThisOutput;
// general parameters
size_t N;
uint32_t root;
uint8_t bid;
uint8_t nChannels;
uint16_t nThreads;
int lastChunkSize;
};
comm:launchModel == PARALLEL
を仮定ncclBarrierEnqueueWait(info->comm)
(enqueue.cc:438) で呼び出されるcudaLaunchKernel
は enqueue.cc:197 にあり、関数名などは comm->myParams
に格納されているcomm->myParams
に入る値を決定しているのが saveKernel
という関数である次は myParams
に意識して saveKernel
を読んでいく
saveKernel
computeColl
getPatternInfo
は info->pattern
を呼び出されるOpによって決定する(AllReduceなら、これとか)getLoopInfo
は info->nstepsPerLoop
を info->pattern
から決定するgetKernelInfo
は info
から 1) nChannels
と nThreads
と llmode
(low-latency) を決定するgridDim.x
の初期値は 0 (init.cc:577)nChannels
の初期値は nrings
nrings
は transport/p2p.cc で頑張って定義されているNCCL_MAX_OPS
は NCCL Aggregated Operations のためにあるので今回は上限に行かない想定で大丈夫そうchannel->collFifoTail
の初期値は 0 で良さそう int
型だしactivePtr
は aggregate coll の現在のポインタgridDim.x == coll.args.nChannels
になるcoll.args.nChannels
と info->comm->nChannels
が違う?computeColl
を読まないとだめか。。transportSaveProxies(&proxyArgs, info->pattern, info->root, info->comm->nRanks)
が何をしているをのかがわからないな
SaveProxy
を呼んでいるsaveKernel
について読む理由:
CUDAカーネルが実際にローンチされているのは ncclBarrierEnqueueWait
の関数内。
で、このカーネルの実引数は info->comm->myParams
が渡されてる
で、この info->comm->myParams
は saveKernel
内で色々定義されている
init.cc:576: params->blockDim.x = 0; params->blockDim.y = params->blockDim.z = 1;
変更されるのは blockDim.x
のみ、 blockDim.y
と blockDim.z
は常に 1 に固定
init.cc:577: params->gridDim.x = 0; params->gridDim.y = params->gridDim.z = 1;
変更されるのは blockDim.x
のみ、 blockDim.y
と blockDim.z
は常に 1 に固定
struct ncclChannel {
union {
struct {
struct ncclRing ring;
struct ncclTree tree;
int id;
int nthreads;
int buffSize;
// Communication structures
struct ncclPeer* peers;
struct ncclPeer* devPeers;
// Operation list for aggregation
struct ncclColl* collectives;
struct ncclColl* devCollectives;
int collStart;
int collCount;
int collFifoHead; // Only used by GPU
int collFifoTail; // Only used by CPU
};
int data[0x80];
};
};
static_assert(sizeof(struct ncclChannel) == 0x80*sizeof(int), "ncclChannel must have a pow2 size");
info->comm->myParams->gridDim.x == coll.args.nChannels
が成立
が普通に利用する場合は gridDim.x == nChannels
が成立する
coll.args.nChannels <= info->comm->nChannels
が成立
coll.args.nChannels
は saveKernel()
内の computeColl()
で定義される
info->comm->nChannels
は ncclInitRank()
内の ncclGetRings()
で定義される
info->comm->myParams->blockDim.x >= coll.args.nThreads
が成立
が普通に利用する場合は blockDim.x == nThreads - 1
が成立する
For 文の中身を呼んでいるが、
struct ncclChannel* channel = info->comm->channels+(info->comm->myParams->gridDim.x % info->comm->nChannels);
ここで、同じ channel
になりなえない気がする。んー
struct ncclProxyArgs {
proxyProgressFunc_t progress;
struct ncclChannel* channel;
struct ncclConnector* connector;
int sliceSteps;
int chunkSteps;
int nsteps;
uint64_t opCount;
int llMode;
int state; // add component before this line -- it is left out during initialization
// Internal state
uint64_t head;
uint64_t tail;
uint64_t end;
void* requests[NCCL_STEPS];
int idle;
// Element linking
pthread_mutex_t mutex;
struct ncclProxyArgs* next;
struct ncclProxyArgs* nextPeer;
};
煮詰まってきたのでここまでのを整理
ncclAllReduce()
が呼ばれ ncclInfo
型の info
が引数として ncclEnqueueCheck()
が呼ばれるncclEnqueueCheck()
内の saveKernel()
が myParams
と channels
に必要な情報を入れるncclEnqueueCheck()
内の ncclBarrierEnqueueWait()
が comm->myParams
を引数としてカーネルを起動するcomm->channels
のすべてのチャンネルが起動される(起動という言葉が正しいかわからない)transportStartProxy()
の呼び出しで完了comm->myParams
には直接代入しないcomm->myParams->blockDim.x = nThreads - 1
( nThreads
は ncclInitRank
内部で定義済) とcomm->myParams->gridDim.x = nChannels
( nChannels
は ncclInitRank
内部で定義済) だけ直接代入されるcoll
to proxyArgs
を利用するcoll
は info
のデータと Grouping コール用のデータを両方持っているproxyArgs
は低レーヤー?の通信用のデータを持っている(nsteps, sliceSteps, chunkSteps, llmode, opCount) などcoll
と proxyArgs
への適切な値の代入は computeColl()
でなされるncclInitRank
とは別に計算するcomm->channels
の各チャンネルに proxyArgs
と coll
の情報を代入 ( transportSaveProxies
かな?)transportStartProxy
が何をしているのか? → CUDAカーネルの起動とは別の何かを起動する必要がある?transportSaveProxies
が何をしているのか? → どのように代入されるのか?step
, slice
, and chunk
???
疑問
ncclAsyncMode
とは`llmode
とはNCCL_MAX_OPS
とはAllReduce の動作
AllReduce の main
ncclCommInitRank
ncclAllReduce
info = ncclCommInfo(...)
collectives/all_reduce.cc#L14ncclEnqueueCheck(&info)
ncclCommDestroy
ncclEnqueueCheck
enqueue.cc#L409ncclAsyncModel
ArgsCheck(info)
saveKernel(info)
ncclBarrierEnqueue(info->comm)
ncclBarrierEnqueueWait(info->comm)
ncclEnqueueEvents(info->comm)
saveKernel(info)
enqueue.cc#L356computeColl(info, &coll, &proxyArgs)
→ 何をしている?blockDim.x
を指定 → 何をしている?for (int bid=0; bid<coll.args.nChannels, bid++)
For 文の中身
まだ良くわからない
nChannels
だけ → 何を意味している?computeColl(info, &coll, &proxyArgs)
info
からcoll
へ情報(root, count, send/recv buffers, devComm, opCount)をコピーinfo
からproxyArgs
へ情報(nsteps, sliceSteps, chunkSteps, llMode, opCount)を計算及びコピーncclBarrierEnqueue(info->comm)
基本的には User stream (nccl の通信を呼び出すストリーミ?、デフォルトストリーミかな)、とパラメータで指定されたストリーム(グループの場合もある)の違いによって、
cudaStreamWaitEvent
を呼び出すようになっているncclCpuBarrierIn
→ わからないncclCpuBarrierLast
→ わからないこのあとに
if(isLast)
があるので、intra のプロセスを待ち、最後のプロセスが処理をするようになっているこんな処理
ncclBarrierEnqueueWait(info->comm)
ncclCpuBarrierOut(comm)
ncclBarrierEnqueue
でカーネルが呼びされていだが、 PARALLEL の場合はここで呼び出されるcomm->channels
に格納されている各 channel に対して次の二つのを定義collStart = channel->collFifiTail
collCount = 0
NCCLCHECK(transportStartProxy(comm));
ncclEnqueueEvents(comm)
必要ならストリームを待つのと、
userStreamSet
を初期化するけっこう単純な関数