示例
本节中的示例提供了如何在各种环境中使用 MCCL 的总体视图,结合一种或多种技术:
- 每个线程/进程使用多个 GPU
- 使用多个线程
- 使用多个进程 - 多个进程的示例使用 MPI 作为并行运行时环境,但任何多进程系统应该都能类似地工作。
确保您始终检查 MCCL 函数的返回代码。 为了清晰起见,以下示例不包含错误检查。
通信器创建和销毁示例
以下示例展示了 MCCL 初始化的常见用例。
示例 1:单进程,单线程,多个设备
在单进程的特定情况下,可以使用 mcclCommInitAll。
这里是一个创建 4 个设备的通信器的示例,因此,
有 4 个通信器对象:
mcclComm_t comms[4];
int devs[4] = { 0, 1, 2, 3 };
mcclCommInitAll(comms, 4, devs);
接下来,您可以使用单个线程和 组调用来调用 MCCL 集体操作, 或者使用多个线程,每个线程都提供一个 comm 对象。
在程序结束时,销毁所有通信器对象:
for (int i=0; i<4; i++)
mcclCommDestroy(comms[i]);
相关链接:mcclCommDestroy
以下代码展示了一个完整的工作示例,其中单个进程管理多个设备:
#include <stdlib.h>
#include <stdio.h>
#include "musa_runtime.h"
#include "mccl.h"
#define MUSACHECK(cmd) do { \
musaError_t err = cmd; \
if (err != musaSuccess) { \
printf("Failed: musa error %s:%d '%s'\n", \
__FILE__,__LINE__,musaGetErrorString(err)); \
exit(EXIT_FAILURE); \
} \
} while(0)
#define MCCLCHECK(cmd) do { \
mcclResult_t res = cmd; \
if (res != mcclSuccess) { \
printf("Failed, MCCL error %s:%d '%s'\n", \
__FILE__,__LINE__,mcclGetErrorString(res)); \
exit(EXIT_FAILURE); \
} \
} while(0)
int main(int argc, char* argv[])
{
mcclComm_t comms[4];
//管理 4 个设备
int nDev = 4;
int size = 32*1024*1024;
int devs[4] = { 0, 1, 2, 3 };
//为设备缓冲区分配和初始化
float** sendbuff = (float**)malloc(nDev * sizeof(float*));
float** recvbuff = (float**)malloc(nDev * sizeof(float*));
musaStream_t* s = (musaStream_t*)malloc(sizeof(musaStream_t)*nDev);
for (int i = 0; i < nDev; ++i) {
MUSACHECK(musaSetDevice(i));
MUSACHECK(musaMalloc((void**)sendbuff + i, size * sizeof(float)));
MUSACHECK(musaMalloc((void**)recvbuff + i, size * sizeof(float)));
MUSACHECK(musaMemset(sendbuff[i], 1, size * sizeof(float)));
MUSACHECK(musaMemset(recvbuff[i], 0, size * sizeof(float)));
MUSACHECK(musaStreamCreate(s+i));
}
//初始化 MCCL
MCCLCHECK(mcclCommInitAll(comms, nDev, devs));
//调用 MCCL 通信 API。当使用
//每个线程的多个设备时需要组 API
MCCLCHECK(mcclGroupStart());
for (int i = 0; i < nDev; ++i)
MCCLCHECK(mcclAllReduce((const void*)sendbuff[i], (void*)recvbuff[i], size, mcclFloat, mcclSum,
comms[i], s[i]));
MCCLCHECK(mcclGroupEnd());
//在 MUSA stream 上同步以等待 MCCL 操作完成
for (int i = 0; i < nDev; ++i) {
MUSACHECK(musaSetDevice(i));
MUSACHECK(musaStreamSynchronize(s[i]));
}
//free 设备缓冲区
for (int i = 0; i < nDev; ++i) {
MUSACHECK(musaSetDevice(i));
MUSACHECK(musaFree(sendbuff[i]));
MUSACHECK(musaFree(recvbuff[i]));
}
//完成 MCCL
for(int i = 0; i < nDev; ++i)
mcclCommDestroy(comms[i]);
printf("Success \n");
return 0;
}