NCCL源码解析①:初始化及ncclUniqueId的产生

作者|KIDGINBROOK
更新|潘丽晨
int e = cmd; \if( e != MPI_SUCCESS ) { \printf("Failed: MPI error %s:%d '%d'\n", \__FILE__,__LINE__, e); \exit(EXIT_FAILURE); \} \} while(0)cudaError_t e = cmd; \if( e != cudaSuccess ) { \printf("Failed: Cuda error %s:%d '%s'\n", \__FILE__,__LINE__,cudaGetErrorString(e)); \exit(EXIT_FAILURE); \} \} while(0)ncclResult_t r = cmd; \if (r!= ncclSuccess) { \printf("Failed, NCCL error %s:%d '%s'\n", \__FILE__,__LINE__,ncclGetErrorString(r)); \exit(EXIT_FAILURE); \} \} while(0)static uint64_t getHostHash(const char* string) {// Based on DJB2a, result = result * 33 ^ charuint64_t result = 5381;for (int c = 0; string[c] != '\0'; c++){result = ((result << 5) + result) ^ string[c];}return result;}static void getHostName(char* hostname, int maxlen) {gethostname(hostname, maxlen);for (int i=0; i< maxlen; i++) {if (hostname[i] == '.') {hostname[i] = '\0';return;}}}int main(int argc, char* argv[]){int size = 32*1024*1024;int myRank, nRanks, localRank = 0;//initializing MPIMPICHECK(MPI_Init(&argc, &argv));MPICHECK(MPI_Comm_rank(MPI_COMM_WORLD, &myRank));MPICHECK(MPI_Comm_size(MPI_COMM_WORLD, &nRanks));//calculating localRank which is used in selecting a GPUuint64_t hostHashs[nRanks];char hostname[1024];getHostName(hostname, 1024);hostHashs[myRank] = getHostHash(hostname);MPICHECK(MPI_Allgather(MPI_IN_PLACE, 0, MPI_DATATYPE_NULL, hostHashs, sizeof(uint64_t), MPI_BYTE, MPI_COMM_WORLD));for (int p=0; pif (p == myRank) break;if (hostHashs[p] == hostHashs[myRank]) localRank++;}//each process is using two GPUsint nDev = 2;float** sendbuff = (float**)malloc(nDev * sizeof(float*));float** recvbuff = (float**)malloc(nDev * sizeof(float*));cudaStream_t* s = (cudaStream_t*)malloc(sizeof(cudaStream_t)*nDev);//picking GPUs based on localRankfor (int i = 0; i < nDev; ++i) {CUDACHECK(cudaSetDevice(localRank*nDev + i));CUDACHECK(cudaMalloc(sendbuff + i, size * sizeof(float)));CUDACHECK(cudaMalloc(recvbuff + i, size * sizeof(float)));CUDACHECK(cudaMemset(sendbuff[i], 1, size * sizeof(float)));CUDACHECK(cudaMemset(recvbuff[i], 0, size * sizeof(float)));CUDACHECK(cudaStreamCreate(s+i));}ncclUniqueId id;ncclComm_t comms[nDev];//generating NCCL unique ID at one process and broadcasting it to allif (myRank == 0) ncclGetUniqueId(&id);MPICHECK(MPI_Bcast((void *)&id, sizeof(id), MPI_BYTE, 0, MPI_COMM_WORLD));//initializing NCCL, group API is required around ncclCommInitRank as it is//called across multiple GPUs in each thread/processNCCLCHECK(ncclGroupStart());for (int i=0; iCUDACHECK(cudaSetDevice(localRank*nDev + i));NCCLCHECK(ncclCommInitRank(comms+i, nRanks*nDev, id, myRank*nDev + i));}NCCLCHECK(ncclGroupEnd());//calling NCCL communication API. Group API is required when using//multiple devices per thread/processNCCLCHECK(ncclGroupStart());for (int i=0; iNCCLCHECK(ncclAllReduce((const void*)sendbuff[i], (void*)recvbuff[i], size, ncclFloat, ncclSum,comms[i], s[i]));NCCLCHECK(ncclGroupEnd());//synchronizing on CUDA stream to complete NCCL communicationfor (int i=0; iCUDACHECK(cudaStreamSynchronize(s[i]));//freeing device memoryfor (int i=0; iCUDACHECK(cudaFree(sendbuff[i]));CUDACHECK(cudaFree(recvbuff[i]));}//finalizing NCCLfor (int i=0; incclCommDestroy(comms[i]);}//finalizing MPIMPICHECK(MPI_Finalize());printf("[MPI Rank %d] Success \n", myRank);return 0;}
ncclResult_t ncclGetUniqueId(ncclUniqueId* out) {NCCLCHECK(ncclInit());NCCLCHECK(PtrCheck(out, "GetUniqueId", "out"));return bootstrapGetUniqueId(out);}
ncclResult_t initNet() {// Always initialize bootstrap networkNCCLCHECK(bootstrapNetInit());NCCLCHECK(initNetPlugin(&ncclNet, &ncclCollNet));if (ncclNet != NULL) return ncclSuccess;if (initNet(&ncclNetIb) == ncclSuccess) {ncclNet = &ncclNetIb;} else {NCCLCHECK(initNet(&ncclNetSocket));ncclNet = &ncclNetSocket;}return ncclSuccess;}
static int findInterfaces(const char* prefixList, char* names, union socketAddress *addrs, int sock_family, int maxIfNameSize, int maxIfs) {struct netIf userIfs[MAX_IFS];bool searchNot = prefixList && prefixList[0] == '^';if (searchNot) prefixList++;bool searchExact = prefixList && prefixList[0] == '=';if (searchExact) prefixList++;int nUserIfs = parseStringList(prefixList, userIfs, MAX_IFS);int found = 0;struct ifaddrs *interfaces, *interface;getifaddrs(&interfaces);for (interface = interfaces; interface && found < maxIfs; interface = interface->ifa_next) {if (interface->ifa_addr == NULL) continue;int family = interface->ifa_addr->sa_family;if (family != AF_INET && family != AF_INET6)continue;if (sock_family != -1 && family != sock_family)continue;if (family == AF_INET6) {struct sockaddr_in6* sa = (struct sockaddr_in6*)(interface->ifa_addr);if (IN6_IS_ADDR_LOOPBACK(&sa->sin6_addr)) continue;}if (!(matchIfList(interface->ifa_name, -1, userIfs, nUserIfs, searchExact) ^ searchNot)) {continue;}bool duplicate = false;for (int i = 0; i < found; i++) {if (strcmp(interface->ifa_name, names+i*maxIfNameSize) == 0) { duplicate = true; break; }}if (!duplicate) {strncpy(names+found*maxIfNameSize, interface->ifa_name, maxIfNameSize);int salen = (family == AF_INET) ? sizeof(sockaddr_in) : sizeof(sockaddr_in6);memcpy(addrs+found, interface->ifa_addr, salen);found++;}}freeifaddrs(interfaces);return found;}
ncclResult_t ncclIbInit(ncclDebugLogger_t logFunction) {static int shownIbHcaEnv = 0;if(wrap_ibv_symbols() != ncclSuccess) { return ncclInternalError; }if (ncclParamIbDisable()) return ncclInternalError;if (ncclNIbDevs == -1) {pthread_mutex_lock(&ncclIbLock);wrap_ibv_fork_init();if (ncclNIbDevs == -1) {ncclNIbDevs = 0;if (findInterfaces(ncclIbIfName, &ncclIbIfAddr, MAX_IF_NAME_SIZE, 1) != 1) {WARN("NET/IB : No IP interface found.");return ncclInternalError;}// Detect IB cardsint nIbDevs;struct ibv_device** devices;// Check if user defined which IB device:port to usechar* userIbEnv = getenv("NCCL_IB_HCA");if (userIbEnv != NULL && shownIbHcaEnv++ == 0) INFO(NCCL_NET|NCCL_ENV, "NCCL_IB_HCA set to %s", userIbEnv);struct netIf userIfs[MAX_IB_DEVS];bool searchNot = userIbEnv && userIbEnv[0] == '^';if (searchNot) userIbEnv++;bool searchExact = userIbEnv && userIbEnv[0] == '=';if (searchExact) userIbEnv++;int nUserIfs = parseStringList(userIbEnv, userIfs, MAX_IB_DEVS);if (ncclSuccess != wrap_ibv_get_device_list(&devices, &nIbDevs)) return ncclInternalError;for (int d=0; dstruct ibv_context * context;if (ncclSuccess != wrap_ibv_open_device(&context, devices[d]) || context == NULL) {WARN("NET/IB : Unable to open device %s", devices[d]->name);continue;}int nPorts = 0;struct ibv_device_attr devAttr;memset(&devAttr, 0, sizeof(devAttr));if (ncclSuccess != wrap_ibv_query_device(context, &devAttr)) {WARN("NET/IB : Unable to query device %s", devices[d]->name);if (ncclSuccess != wrap_ibv_close_device(context)) { return ncclInternalError; }continue;}for (int port = 1; port <= devAttr.phys_port_cnt; port++) {struct ibv_port_attr portAttr;if (ncclSuccess != wrap_ibv_query_port(context, port, &portAttr)) {WARN("NET/IB : Unable to query port %d", port);continue;}if (portAttr.state != IBV_PORT_ACTIVE) continue;if (portAttr.link_layer != IBV_LINK_LAYER_INFINIBAND&& portAttr.link_layer != IBV_LINK_LAYER_ETHERNET) continue;// check against user specified HCAs/portsif (! (matchIfList(devices[d]->name, port, userIfs, nUserIfs, searchExact) ^ searchNot)) {continue;}TRACE(NCCL_INIT|NCCL_NET,"NET/IB: [%d] %s:%d/%s ", d, devices[d]->name, port,portAttr.link_layer == IBV_LINK_LAYER_INFINIBAND ? "IB" : "RoCE");ncclIbDevs[ncclNIbDevs].device = d;ncclIbDevs[ncclNIbDevs].guid = devAttr.sys_image_guid;ncclIbDevs[ncclNIbDevs].port = port;ncclIbDevs[ncclNIbDevs].link = portAttr.link_layer;ncclIbDevs[ncclNIbDevs].speed = ncclIbSpeed(portAttr.active_speed) * ncclIbWidth(portAttr.active_width);ncclIbDevs[ncclNIbDevs].context = context;strncpy(ncclIbDevs[ncclNIbDevs].devName, devices[d]->name, MAXNAMESIZE);NCCLCHECK(ncclIbGetPciPath(ncclIbDevs[ncclNIbDevs].devName, &ncclIbDevs[ncclNIbDevs].pciPath, &ncclIbDevs[ncclNIbDevs].realPort));ncclIbDevs[ncclNIbDevs].maxQp = devAttr.max_qp;ncclNIbDevs++;nPorts++;pthread_create(&ncclIbAsyncThread, NULL, ncclIbAsyncThreadMain, context);}if (nPorts == 0 && ncclSuccess != wrap_ibv_close_device(context)) { return ncclInternalError; }}if (nIbDevs && (ncclSuccess != wrap_ibv_free_device_list(devices))) { return ncclInternalError; };}if (ncclNIbDevs == 0) {INFO(NCCL_INIT|NCCL_NET, "NET/IB : No device found.");} else {char line[1024];line[0] = '\0';for (int d=0; dsnprintf(line+strlen(line), 1023-strlen(line), " [%d]%s:%d/%s", d, ncclIbDevs[d].devName,ncclIbDevs[d].port, ncclIbDevs[d].link == IBV_LINK_LAYER_INFINIBAND ? "IB" : "RoCE");}line[1023] = '\0';char addrline[1024];INFO(NCCL_INIT|NCCL_NET, "NET/IB : Using%s ; OOB %s:%s", line, ncclIbIfName, socketToString(&ncclIbIfAddr.sa, addrline));}pthread_mutex_unlock(&ncclIbLock);}return ncclSuccess;}
ncclResult_t bootstrapCreateRoot(ncclUniqueId* id, bool idFromEnv) {ncclNetHandle_t* netHandle = (ncclNetHandle_t*) id;void* listenComm;NCCLCHECK(bootstrapNetListen(idFromEnv ? dontCareIf : 0, netHandle, &listenComm));pthread_t thread;pthread_create(&thread, NULL, bootstrapRoot, listenComm);return ncclSuccess;}
static ncclResult_t bootstrapNetListen(int dev, ncclNetHandle_t* netHandle, void** listenComm) {union socketAddress* connectAddr = (union socketAddress*) netHandle;static_assert(sizeof(union socketAddress) < NCCL_NET_HANDLE_MAXSIZE, "union socketAddress size is too large");// if dev >= 0, listen based on devif (dev >= 0) {NCCLCHECK(bootstrapNetGetSocketAddr(dev, connectAddr));} else if (dev == findSubnetIf) {...} // Otherwise, handle stores a local addressstruct bootstrapNetComm* comm;NCCLCHECK(bootstrapNetNewComm(&comm));NCCLCHECK(createListenSocket(&comm->fd, connectAddr));*listenComm = comm;return ncclSuccess;}
static ncclResult_t bootstrapNetGetSocketAddr(int dev, union socketAddress* addr) {if (dev >= bootstrapNetIfs) return ncclInternalError;memcpy(addr, bootstrapNetIfAddrs+dev, sizeof(*addr));return ncclSuccess;}
struct bootstrapNetComm {int fd;};
static ncclResult_t createListenSocket(int *fd, union socketAddress *localAddr) {/* IPv4/IPv6 support */int family = localAddr->sa.sa_family;int salen = (family == AF_INET) ? sizeof(sockaddr_in) : sizeof(sockaddr_in6);/* Create socket and bind it to a port */int sockfd = socket(family, SOCK_STREAM, 0);if (sockfd == -1) {WARN("Net : Socket creation failed : %s", strerror(errno));return ncclSystemError;}if (socketToPort(&localAddr->sa)) {// Port is forced by env. Make sure we get the port.int opt = 1;SYSCHECK(setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt)), "setsockopt");SYSCHECK(setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)), "setsockopt");}// localAddr port should be 0 (Any port)SYSCHECK(bind(sockfd, &localAddr->sa, salen), "bind");/* Get the assigned Port */socklen_t size = salen;SYSCHECK(getsockname(sockfd, &localAddr->sa, &size), "getsockname");char line[1024];TRACE(NCCL_INIT|NCCL_NET,"Listening on socket %s", socketToString(&localAddr->sa, line));/* Put the socket in listen mode* NB: The backlog will be silently truncated to the value in /proc/sys/net/core/somaxconn*/SYSCHECK(listen(sockfd, 16384), "listen");*fd = sockfd;return ncclSuccess;}
(本文经授权后由OneFlow发布。原文:https://blog.csdn.net/KIDGIN7439/article/details/126712106?spm=1001.2014.3001.5502)
其他人都在看 点击“阅读原文”,欢迎Star、试用OneFlow新版本

-
专业算力再进化:宽恒科技携手 RTX PRO 5000 Blackwell 深耕行业市场
当 AI 从云端走向桌面,专业显卡正迎来新一轮黄金周期。今年春季 GTC 大会上,NVIDIA 正式发布 RTX PRO 5000 Blackwell 专业显卡,凭借 Blackwell 架构与 48GB/72GB 超高速 GDDR7 显存,一经亮相便成为工作站市场的焦点;10 月 20 日,高盛再度上调全球 AI 服务器出货预测,AI 算力需求的旺盛可见一斑 —— 而在个体与团队层面,越来越多设计师、工程师与 AI 开发者,正在把 "专业算力" 作为生产力的核心投资。专业显卡市场,正从幕后走向台前。
넶0 2026-10-20 -
桌面上的 AI 超算:宽恒科技解析 NVIDIA DGX Spark 的应用之道
AI 算力的形态,正在发生一场 "下沉" 革命。10 月 2 日,NVIDIA 发布 DGX Spark 64GB,为开发者提供更多方式构建和扩展本地 AI;而在年初的 CES 2026 上,NVIDIA DGX Spark 驱动机器人 Reachy Mini 在展台与观众实时互动,把 "桌面级 AI 超算" 的想象力具象化。就在同一时期,全球首部面向智能体安全的强制性国家标准正式立项,AI 智能体从实验室走向生产环境已成定局。当智能体成为新的软件形态,一个现实问题浮出水面:海量的本地推理与智能体运行,是否都要依赖云端?NVIDIA DGX Spark 给出的答案是 —— 把 AI 超算放进桌面。
넶0 2026-10-20 -
PICO 4 Ultra 与 PICO OS 6 共舞,宽恒科技带您进入空间计算新时代
空间计算的浪潮,正从概念走向现实。2026 年下半年,PICO 接连放出重磅消息:面向开发者先行提供下一代操作系统 PICO OS 6,并预告代号 "Project Swan" 的新一代旗舰 XR 硬件将在全球发布,其超高精显示屏达到约 4000PPI 的量级,引发行业广泛关注。与此同时,PICO 4 Ultra 作为当前在售的主力 MR 一体机,凭借成熟的硬件与不断进化的软件生态,依然是大众体验空间计算的最佳入口之一。从娱乐到办公,从教育到行业应用,以 PICO 4 Ultra 为代表的 MR 设备,正在重新定义人与数字世界的交互方式。
넶0 2026-10-20 -
英伟达 NPN 生态扩容,宽恒科技深耕英伟达代理与合作伙伴服务
英伟达的生态版图正在加速扩张。8 月 31 日,英伟达宣布与 MediaTek 深化长期合作,共同构建从边缘到云端的 AI 计算平台,MediaTek 将采用全新的 NVLink Fusion 平台,帮助客户将定制 XPU 引入机架级 AI 工厂;10 月 14 日,英伟达又携手应用材料、Cadence、KLA、Lam Research、西门子、新思科技等半导体领军企业,推动 AI 在芯片设计与制造全流程落地。一连串动作背后,是英伟达从 "卖芯片" 向 "卖生态" 的战略深化 —— 而承载这一战略的市场通道,正是不断升级的 NVIDIA Partner Network(NPN,英伟达合作伙伴网络)。
넶0 2026-10-20 -
算力租赁驶入快车道,宽恒科技以 GPU 集群与 AI 服务器构筑算力新基建
刚刚过去的十月,算力市场再迎重磅消息。10 月 20 日,高盛大幅上调全球 AI 服务器出货预测,预计 2026 至 2028 年八卡 AI 服务器出货量将分别达到 173 万台、265.5 万台与 330.5 万台,隐含的 AI 芯片需求逼近两千万颗量级。来自中国信通院的另一组数据更加直观:2026 年一季度,国内算力租赁市场规模已达 680 亿元,全年预计突破 2600 亿元;同期国内 AI 算力需求同比大增 417%,而供给增速仅有 128%。需求狂奔、供给承压,"算力焦虑" 正从科技圈蔓延至千行百业。与此同时,政府工作报告明确部署超大规模智算集群、算电协同等新基建工程,一场围绕算力资源的产业变革正加速上演。
넶0 2026-10-20 -
宽恒科技:英伟达显卡总代视角下的 RTX PRO 5000 Blackwell
2026 年,AIGC 工作流已经深入设计、影视、工业与科研的每一个角落。Stable Diffusion 出图、本地大模型推理、数字孪生渲染…… 这些负载对显卡的显存容量与计算性能提出了前所未有的要求,专业显卡市场随之迎来一轮 “大显存、高算力” 的升级潮。在这一轮升级中,英伟达 Blackwell 架构的 RTX PRO 5000 专业显卡成为工作站用户关注的焦点,也让 “专业卡替代游戏卡跑生产负载” 成为行业讨论的常驻话题。
넶2 2026-10-19