当前位置: 首页 > news >正文

nccl-1_initialization bootstrap

nccl学习-1: 初始化communicator与bootstrap 网络

在nccl的例子中,当我们获取了设备的数量,为每一个设备分配了一个ncclComm_t后,需要根据已经固定的通信拓扑,对每一个算子进行配置。目前,针对communicator的初始化,我们具有四个函数:

  • ncclCommInitAll
  • ncclCommInitRank
  • ncclCommInitRankConfig
  • ncclCommInitRankScalable

接下来我们大致了解一下各个算子。

ncclCommInitAll

利用一个进程,通过阻塞的方式创建一个通信群,将会为传入的comm指针进行初始化,通过一个进程来实现多个设备的communicator的初始化。

我们进入ncclCommInitAll中。首先我们将初始化Nvtx(用于profiling)相关参数的payload,并且初始化cudaLibrary(加载动态库,设置相关的设备,获取驱动等工作)。在这里,初始化采用了 std::call_once 操作。这个操作保证了在一台设备上,多个线程/进程只会调用该函数一次。如果发生异常则将调用权转移给其他线程/进程,这样他们就可以继续调用直到成功或者失败退出。

C++: std::call_once在libc++中的实现
std::call_once 通过一个 once_flag 结构,提供三种状态: 0: 未初始化。 1: 进行中。 2: 调用成功返回。在其中一个线程正在执行时,原子操作flag并且获取锁来保证线程唯一执行。

通过exception_guard,利用RAII特性,在抛出异常析构时调用guard的析构函数,释放锁同时将flag进行更改,将其修改成为“未初始化”模式,保证其他线程/进程能重新获得锁进行执行。

通过信号量来减少CPU占用。一个模拟的实现: call_once

在获取设备,进行指针检查和节点设备数量后,如果传入了对应的devList(用于确定节点内设备的rank号),则判断是否合法。否则接下来开始bootstrap网络,获取通信域,也即将开始进入核心部分:bootstrap 网络。

BootStrap network

ncclGetUniqueId

这部分将会为我们创建一个 ncclUniqueId, 用于标识我们的一个通信域。

首先我们执行ncclInitEnv。这一部分主要用于载入对应的插件/网络结构的动态库。例如ext-net中我们可以插入 collNet 用于在网集合通信运算,提升跨节点的一些集合通信速度。ext-profiler用于性能检测,ext-tuner用于性能调优。同样是call_once.

接着我们执行ncclInit。这一部分将执行三个部份,同样是call_once。

  • 设置CPU栈的大小。对于nccl,他需要至少8M的安全栈空间。
  • 初始化GdrCopy。加载gdr的相关内核模块和驱动。
  • 初始化BootStrap网络。

我们来观察初始化bootStrap网络的过程。


  • bootstrapNetInit

  • 首先,读取环境变量中的设定NCCL_COMM_ID=<ipv4>:<port>,我们可以手动通过这个方式来设置通信子的相关信息。这样直接获取了信息后,将socket的相关地址转换成string并且在子网中进行匹配。这种使用的情况不多,因此我们不再赘述。

  • 当没有设定上述环境变量的时候,我们就需要自行寻找interfaces了,执行ncclFindInterfaces来寻找对应的接口信息。


  • ncclFindInterfaces

根据我们设定的socket来寻找,或者直接寻找所有的网卡。这里我们分别进行介绍。

  • 在设置了 NCCL_SOCKET_IFNAME后,首先将会根据设定的网卡名进行前缀匹配。=为完全匹配,^则代表排除该网口。在寻找到网口后,存储他们的ipv4/ipv6信息。
  • 否则,在没有设置的情况下,将会优先寻找ib网口->排除docker, lo, virbr寻找->寻找docker,lo,virbr网口。
  • findInterfaces函数将会把网卡信息逐个进行填写。
    执行完成后我们获得了多个网口的信息(网口名+ip地址),返回 bootstrapNetInit

  • bootstrapNetInit

在上述函数将网卡写好后,我们将网卡名转换成字符记录在log中,返回。于是初始化bootstrap网络完成。

接下来我们将真正从我们的bootstrap网络中获取UniqueID。进入bootStrapGetUniqueId.
首先我们已经在bootstrapNetIfAddr中初始化了一系列的网口信息。因此,我们将会用第一个网口作为生成我们的ID的网口。我们可以看到:

  • 首先我们利用随机数生成器,通过硬件从环境中获取噪声后生成。通过读取/dev/urandom获取一个uinteger,填写到state->magic中。
  • 随后将bootstrapNetIfAddr中的第一个网卡地址读取到handle -> addr中。
  • 开始执行 bootstrapCreateRoot

  • bootstrapCreateRoot

将第一个网卡作为监听网卡,并且创建BootStrapRoot进程,该进程该进程将会在background进行。我们可以看到执行了pthread_detach操作。于是主进程将直接返回继续其他工作。笔者的blog不能并发,因此我们会先进入到这其中。

进入bootstrapRoot中,我们准备好如下的相关数据:

  • bootstrap相关:参数(刚刚生成的magic,以及确定的监听socket信息)
  • bootstrap拓扑相关:nranks(总进程rank数量),iroot(其他进程的root),nroots(多少个root,多少个node进程),localID(iroot下进程的rank),n2send(将要传输给的进程的rank)和nrecv(将要接受数据的进程的rank),c(完成信息注册的其他进程的轮数)。
  • info(root获取的特定进程的通信信息,包括了他监听root的socket地址和他进行p2p通信的地址)。
  • ringConnectInfo(p2p之间进行通信的相关信息)。
  • ncclSocketAddress (各个进程用于与root进程通信的地址)。
  • 一个全零数据用于判断是否已经进行初始化。

接下来我们开始进行初始化过程。接下来是比较复杂的流程,需要更加耐心地理解。在初始化中,需要完成以下的目标:

  • 每个root节点需要了解整体的rank信息和自己的子节点的相关信息。
  • 告诉每个子节点ring逻辑拓扑关系,帮助他们建立p2p连接。

首先,我们初始化sock,建立与Root主进程中的listenSocket连接后(其实就是复制fd),开始等待其他进程发来的信息。这里我们发现,在创建sock的时候,async_flag设置成了阻塞模式,因此会在建立连接(过渡过程中等待完成)。这一部分在ncclSocketAccept中执行。

ncclSocketAccept中,(1)首先将复制listenSocket的相关内容,随后开始尝试进行accept,进入socketProgressState中。在阻塞模式下,我们将等待直到完成。(2)在ncclSocketStateAccepting状态下,socketTryAccept将首先执行,用于在OS层面建立TCP连接。在获得对应的套接字fd后,我们将返回,说明完成了TCP连接。状态转换成ncclSocketStateAccepted。
(3)接着,继续执行socketFinalizeAccept。在这一步我们将会判断当前的socket是否真正地和主进程建立了一个连接。通过读取fd的方式来判断TCP连接是否合法,我们需要检查:1. fd中的magic是否正确,也就是主进程和线程的magic是否相同(判断TCP连接是否正常)。2. 判断socket类型是否相同。判断当前的socket是否为我们需要的ncclSocketTypeBootstrap,而非其他的服务socket。(4)在判断完成后,我们将进入ncclSocketRecv,循环等待其他进程的连接。

这时,我们收到了来自其他进程的连接后,首次获得了获得了其他进程的info信息。这里的info信息包含了:1.进程的rank。2.进程对应的root(iroot)。3. 进程中存储的系统nranks信息(有多少进程)和nroots(有多少root节点),4. 用于监听root信息的socket信息,5.用于p2p交换信息的socket信息。

img

当第一个rank进入的时候,也就是 c==0,我们需要获取系统的整体信息。也就是nranks,nroots,并且和第一个iroot交换信息(当具有多个root的时候)。所有的root都需要了解自己的子节点信息和系统整体的信息,并且分配rankAddressesRoot用于存储自己管理的子节点信息,分配rankInfo来用于存储自己管理的子节点用于p2p的信息。

计算出对应的节点在其root节点下的rank号后,我们需要判断他是否已经完成过注册。这时前面全零数据就起到了作用。

接下来完善ring通信逻辑,需要判断当前新加入节点在ring逻辑拓扑中的前序节点和后序节点是否有没有加入过。

在前序节点已经加入的情况下:bootStrapRoot线程将直接通过rankAddressesRoot发送当前新加入节点的info给前序节点,供前序节点建立p2p通信。
在后序节点已经加入的情况下:bootStrapRoot线程将会直接通过info中记录的新加入节点监听root的socket,发送自己已经记录的后序节点的rankInfo给新加入的节点,从而帮助当前这两个节点构建p2p连接。
这样最后一个节点始终是最后的,没有收到任何信息的节点。因此将rank 0的信息发送给这个节点。

为了方便理解,我们构建了一个简单的图示。

img

img

在进行bootstrap的过程中,通过ncclCommInitRancFunc函数中的bootstrapInit来构建p2p ring网络和与bootStrapRoot线程通信。此处之后的信息后面我们将会介绍。

TBD...

http://www.jsqmd.com/news/64500/

相关文章:

  • 2025年国内TOP5会计培训机构推荐,引领专业提升之路
  • 2025年国内TOP5会计培训机构推荐,引领专业提升之路
  • 2025年评价高的草本床垫供应厂家TOP5推荐
  • 2025 最新智能运维服务商/ 厂家 TOP5 评测!科技赋能 + 全周期服务权威榜单发布,引领智慧工厂运维新生态
  • 人生第一篇博客:千里之行,始于足下
  • 2025年专业HIFI耳机口碑排行榜推荐,不容错过!
  • 全网热议!2025年比较好的全屋定制公司推荐
  • 全网热议!2025年靠谱的全屋定制品牌推荐,让生活更智能
  • 扩散炉供应厂家TOP推荐:2025年值得关注的优质厂家
  • Ancel AD410 OBD2 Scanner - Multi-Language OBD2 Code Reader Error Eraser for European American Cars
  • 2025年如何选择值得信赖的家居照明公司?
  • 2025年护眼吸顶灯销售厂家有哪些?
  • 豆包碰壁微信,你只看热闹吗?个人数据归属的经济范式、法理解析 与我们的未来
  • 20251206 之所思 - 人生如梦
  • 一些心事
  • 洛谷 P11345 [KTSC 2023 R2] 基地简化 题解
  • Visual Studio Installer 2022正在进行准备
  • 在keil 中使用__attribute__关键字实现静态加载
  • ANCEL AS100 OBD2 Scanner: Full EOBD/OBDII/CAN BMW Check Engine Light Diagnostic Tool
  • 2025最新广东餐饮生鲜配送服务商/厂家TOP5推荐!深圳/广州/佛山/东莞全覆盖,全品类供应+一体化服务权威榜单发布,赋能餐饮企业降本增效新生态
  • FOXWELL NT809BT OBD2 BiDirectional Scan Tool: All Systems Diagnostics + 30+ Resets for EU/US Cars
  • SSM文创产品推荐环境设计与实现95ml5(工具+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,框架界面在末了面。
  • obsidian dataviewjs查找冗余文件
  • 模板索引 字符串
  • 2025.12.6日22:51-patriarchal家长的;族长的;由族长统治的
  • 2025最新深圳餐饮食材配送服务商/厂家TOP5推荐!全品类供应+一体化服务权威榜单发布,赋能餐饮企业降本增效新生态
  • 数据采集与融合技术作业4
  • Last Dance
  • 责任链模式
  • 树基础