NCCL源码解读3.1:double binary tree双二叉树构建算法,相比ring环算法的优势

目录

一、双二叉树出现的原因

二、双二叉树介绍

三、双二叉树大规模性能

四、双二叉树源码解读

双二叉树注意事项

核心逻辑

源码速递


视频分享在这,未完待补充:

3.1 NCCL源码解读双二叉树构建算法,double binary tree相比ring环算法的优_哔哩哔哩_bilibili

一、双二叉树出现的原因

ring环算法,延迟随 GPU 数量线性扩展,从而无法扩展到数百个 GPU 以上。

想象一下,使用数以万计的 GPU 来训练您的神经网络。使用多个 GPU 来训练神经网络在所有深度学习框架中都已变得非常普遍,提供优化的多 GPU 和多机器训练。用于对多个 GPU 上的梯度求和的 Allreduce 操作通常使用环 [1] [2] 来实现,以实现全带宽。环的缺点是延迟随 GPU 数量线性扩展,从而无法扩展到数百个 GPU 以上。进入 NCCL 2.4。

许多大规模实验已经用分层的 2D 环算法 [3] [4] [5] 取代了平面环,以获得相当好的带宽,同时降低延迟。

NCCL 2.4 现在增加了双二叉树,它提供完整的带宽和甚至低于 2D 环形延迟的对数延迟。

二、双二叉树介绍

双二叉树于 2009 年在 MPI 中引入 [6],其优势是将广播和 reduce 操作的全带宽(可以组合成一个执行 reduce,然后执行广播的 allreduce)和对数延迟相结合,从而在中小型操作中实现良好的性能。

在 NCCL 中,我们使用一种易于实现的模式构建二叉树,该模式使局部性最大化,如图 1 所示。

二叉树图

图 1.二叉树

双二叉树依赖于这样一个事实,即二叉树中一半或更少的秩是节点,而一半(或更多)秩是叶。因此,我们可以用叶子作为节点来构建第二棵树,反之亦然。可能有一个等级在两棵树上都是叶子,但没有一个等级是两棵树上的节点。

图 2 显示了如何使用上面的模式通过翻转树来反转节点和叶子来构建双二叉树。

双互补二叉树图

图 2.两个互补的二叉树,其中每个秩最多是一个树中的一个节点和另一个树中的一个叶子。

如果将这两个树叠加,则所有等级都同时具有两个父级和两个子级,但根级除外,根级只有一个父级和一个子级。如果我们用两棵树中的每一棵来处理一半的数据,那么每个 rank 最多会收到两次一半的数据,两次发送一半的数据,这在发送/接收的数据方面就像 rings 一样最优。

三、双二叉树大规模性能

我们在各种大型机器上测试了 NCCL 2.4,包括 Summit [7] 超级计算机,最高可达 24,576 个 GPU。如图 3 所示,使用树时延迟显著改善。与ring的差异随着规模的增加而增加,在 24k GPU 上提升高达 180 倍。

Summit Latency 图表

图 3.NCCL 延迟高达 24,576 个 GPU

我们确认该系统使用双二叉树保持全带宽。在规模上,当我们在 InfiniBand 结构中跨越 L3 交换机时,带宽会略有下降,我们认为这是由于 NCCL 通信模式和 InfiniBand 路由算法之间的效率低下造成的。

虽然并不完美,但将来可能会改进。即便如此,由于初始延迟较小,即使带宽受限,树仍然显示出明显的优势。但是,当该模式导致更大的带宽时,NCCL 会自动切换回环。

峰会带宽图

图 4.多达 24,576 个 GPU 上的 NCCL 总线带宽

四、双二叉树源码解读

4.1 双二叉树注意事项

在正式开始源码解读之前,有几点是需要大家注意的!

待补充。。。。

 4.2 connectTrees()
核心逻辑

1、connectTrees()中调用ncclGetDtree()构建双二叉树

2、ncclGetDtree()调用ncclGetBtree()构建树

这里重点想和大家强调ncclGetDtree()的输入,注意看是node不是rank。

源码速递

源码位置:nccl-2.19\src\graph\connect.cc

static ncclResult_t connectTrees(struct ncclComm* comm, int* treeToParent, int* treeToChild0, int* treeToChild1, int* treePatterns) {const int nChannels = comm->nChannels, nNodes = comm->nNodes, node = comm->node;// Compute tree depth. Not an exact value but a good approximation in most// casesint depth = comm->nRanks/nNodes - 1 + log2i(nNodes);int t0u, t0d0, t0d1, t0ChildType, t1u, t1d0, t1d1, t1ChildType;int* ttp, *ttc0, *ttc1;NCCLCHECK(ncclGetDtree(nNodes, node, &t0u, &t0d0, &t0d1, &t0ChildType, &t1u, &t1d0, &t1d1, &t1ChildType));//其它代码...................}
 4.3 ncclGetDtree() 
核心逻辑

1、调用ncclGetBtree来生成第一棵树

2、nranks是奇数时,通过“移位”操作生成第二颗树。即,每个rank的排名都会向前移动一个位置(通过取模操作处理边界情况),再调用ncclGetBtree来生成第二棵树。

3、nranks是偶数时,通过“镜像”操作生成第二颗树。即,每个rank的排名都会取镜像值(nranks-1-rank),再调用ncclGetBtree来生成第二棵树。

源码速递

源码位置:nccl-2.19\src\graph\trees.cc

/* Build a double binary tree. Take the previous tree for the first tree.* For the second tree, we use a mirror tree (if nranks is even)** 0---------------8                   3----------------11*          ______/ \                 / \______*         4         \               /         7*       /   \        \             /        /   \*     2       6       10         1        5      9*    / \     / \     /  \       / \      / \    / \*   1   3   5   7   9   11     0   2    4   6  8   10** or shift it by one rank (if nranks is odd).** 0---------------8            1---------------9*          ______/ \______              ______/ \______*         4               12           5                0*       /   \            /           /   \            /*     2       6       10           3       7       11*    / \     / \     /  \         / \     / \     /  \*   1   3   5   7   9   11       2   4   6   8  10   12*/
// 定义一个函数,用于获取双通信树。输入参数包括总节点数、当前节点标识符,以及指向存储树信息的指针。ncclResult_t ncclGetDtree(int nranks, int rank, int* s0, int* d0_0, int* d0_1, int* parentChildType0, int* s1, int* d1_0, int* d1_1, int* parentChildType1) {// 首先构建第一个通信树(基础树),使用B树(Binary Tree)的构建方法。// 该步骤填充了s0, d0_0, d0_1, parentChildType0指针所指向的数组。ncclGetBtree(nranks, rank, s0, d0_0, d0_1, parentChildType0);// 接下来构建第二个通信树。根据总节点数nranks的奇偶性,决定是构建镜像树还是位移树。// 如果nranks是奇数,则构建位移树。if (nranks % 2 == 1) {// 计算位移后的节点标识符。这里通过(rank-1+nranks) % nranks来确保结果是非负的。int shiftrank = (rank-1+nranks) % nranks;// 临时变量,用于存储B树构建过程中得到的父节点、两个子节点的标识符。int u, d0, d1;// 使用B树的构建方法,根据位移后的节点标识符来获取位移树的父节点和子节点。ncclGetBtree(nranks, shiftrank, &u, &d0, &d1, parentChildType1);// 由于是位移树,所以需要将得到的节点标识符进行模nranks运算,以确保它们在有效范围内。// 注意这里的+1操作,是因为位移树的构建是基于位移后的节点标识符,而我们需要的是原始标识符空间中的对应值。*s1 = u == -1 ? -1 : (u+1) % nranks;*d1_0 = d0 == -1 ? -1 : (d0+1) % nranks;*d1_1 = d1 == -1 ? -1 : (d1+1) % nranks;} else {// 如果nranks是偶数,则构建镜像树。// 临时变量,用途同上。int u, d0, d1;// 使用B树的构建方法,但这次是根据镜像后的节点标识符(即nranks-1-rank)来获取镜像树的父节点和子节点。ncclGetBtree(nranks, nranks-1-rank, &u, &d0, &d1, parentChildType1);// 由于是镜像树,所以需要将得到的节点标识符进行镜像处理,即使用nranks-1减去对应的标识符。*s1 = u == -1 ? -1 : nranks-1-u;*d1_0 = d0 == -1 ? -1 : nranks-1-d0;*d1_1 = d1 == -1 ? -1 : nranks-1-d1;}// 函数执行成功,返回ncclSuccess。return ncclSuccess;}
 4.4 ncclGetBtree
核心逻辑

1、通过位操作找到rank中的第一个非零位(bit)。

2、rank0是树的根节点,父节点为-1(无父节点),并计算第一个子节点(如果存在)

3、对于非根节点,计算父节点的排名。

4、对于非根节点,计算两个子节点的排名。

源码速递

源码位置:

nccl-2.19\src\graph\trees.cc

/* Btree which alternates leaves and nodes.* Assumes root is 0, which conveniently builds a tree on powers of two,* (because we have pow2-1 ranks) which lets us manipulate bits.* Find first non-zero bit, then :* Find the parent :*   xx01[0] -> xx10[0] (1,5,9 below) or xx00[0] if xx10[0] is out of bounds (13 below)*   xx11[0] -> xx10[0] (3,7,11 below)* Find the children :*   xx10[0] -> xx01[0] (2,4,6,8,10,12) or -1 (1,3,5,7,9,11,13)*   xx10[0] -> xx11[0] (2,4,6,8,10) or xx101[0] (12) or xx1001[0] ... or -1 (1,3,5,7,9,11,13)** Illustration :* 0---------------8*          ______/ \______*         4               12*       /   \            /  \*     2       6       10     \*    / \     / \     /  \     \*   1   3   5   7   9   11    13*/// 定义一个函数,用于获取B树的结构信息。输入参数包括总节点数、当前节点标识符,以及指向存储父节点和两个子节点标识符的指针。
ncclResult_t ncclGetBtree(int nranks, int rank, int* u, int* d0, int* d1, int* parentChildType) {int up, down0, down1; // 声明用于存储父节点和两个子节点标识符的变量int bit; // 声明用于位操作的变量// 通过位操作找到rank中第一个非零位的位置for (bit=1; bit<nranks; bit<<=1) {if (bit & rank) break; // 若bit位与rank的对应位均为1,则跳出循环}// 处理根节点(rank为0)的特殊情况if (rank == 0) {*u = -1; // 根节点没有父节点*d0 = -1; // 根节点可能没有第一个子节点(取决于nranks)// 根节点的第二个子节点(如果存在)是bit右移一位得到的节点*d1 = nranks > 1 ? bit >> 1 : -1;return ncclSuccess; // 函数执行成功}// 根据位操作计算父节点的标识符up = (rank ^ bit) | (bit << 1); // 若rank的bit位为1,则将其翻转并设置更高一位为1,得到父节点的候选值// 若候选父节点标识符超出范围,则使用另一个候选值(仅翻转bit位)if (up >= nranks) up = (rank ^ bit);*parentChildType = (rank < up) ? 0 : 1; // 根据当前节点与父节点的关系设置父子关系类型(0为第一个子节点,1为第二个子节点)*u = up; // 存储父节点标识符int lowbit = bit >> 1; // 计算低位的值(用于确定子节点的位置)// 第一个子节点总是在当前节点的低位侧,但需注意边界情况down0 = lowbit == 0 ? -1 : rank-lowbit;// 第二个子节点的计算需要考虑边界情况,确保其标识符在有效范围内内down1 = lowbit == 0 ? -1 : rank+lowbit;while (down1 >= nranks) { // 若第二个子节点标识符超出范围,则通过不断右移lowbit来尝试修正down1 = lowbit == 0 ? -1 : rank+lowbit;lowbit >>= 1;}// 存储两个子节点的标识符*d0 = down0;*d1 = down1; return ncclSuccess; // 函数执行成功}

参考资料

Massively Scale Your Deep Learning Training with NCCL 2.4 | NVIDIA Technical Blog

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/501717.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

深入理解 JVM 的垃圾收集器:CMS、G1、ZGC

&#x1f9d1; 博主简介&#xff1a;CSDN博客专家&#xff0c;历代文学网&#xff08;PC端可以访问&#xff1a;https://literature.sinhy.com/#/literature?__c1000&#xff0c;移动端可微信小程序搜索“历代文学”&#xff09;总架构师&#xff0c;15年工作经验&#xff0c;…

四、VSCODE 使用GIT插件

VSCODE 使用GIT插件 一下载git插件与git Graph插件二、git插件使用三、文件提交到远程仓库四、git Graph插件 一下载git插件与git Graph插件 二、git插件使用 git插件一般VSCode自带了git&#xff0c;就是左边栏目的图标 在下载git软件后vscode的git插件会自动识别当前项目 …

【NLP高频面题】用RNN训练语言模型时如何计算损失?

用RNN训练语言模型时如何计算损失&#xff1f; 重要性&#xff1a;★ 以“you say goodbye and i say hello.”为例&#xff0c;将其作为具体的数据传入网络&#xff0c;此时 RNNLM 进行的处理如图所示&#xff1a; RNNLM 可以“记忆”目前为止输入的单词&#xff0c;并以此…

Spring Cloud Security集成JWT 快速入门Demo

一、介绍 JWT (JSON Web Token) 是一种带有绑实和信息的简单标准化机制&#xff0c;在信息通信中用于验证和信息传递。尤其在应用中使用Spring Cloud实现分布式构建时&#xff0c;JWT可以作为一种无状态验证原理的证明。 本文将进一步描述如何在Spring Cloud Security中集成JW…

【机器学习】【朴素贝叶斯分类器】从理论到实践:朴素贝叶斯分类器在垃圾短信过滤中的应用

&#x1f31f; 关于我 &#x1f31f; 大家好呀&#xff01;&#x1f44b; 我是一名大三在读学生&#xff0c;目前对人工智能领域充满了浓厚的兴趣&#xff0c;尤其是机器学习、深度学习和自然语言处理这些酷炫的技术&#xff01;&#x1f916;&#x1f4bb; 平时我喜欢动手做实…

unity学习5:创建一个自己的3D项目

目录 1 在unity里创建1个3D项目 1.1 关于选择universal 3d&#xff0c;built-in render pipeline的区别 1.2 创建1个universal 3d项目 2 打开3D项目 2.1 准备操作面板&#xff1a;操作界面 layout,可以随意更换 2.2 先收集资源&#xff1a;打开 window的 AssetStore 下载…

Vue3 内置组件之component

文章目录 Vue3 内置组件之component概述使用 Vue3 内置组件之component 概述 <component> 组件提供了动态组件加载功能&#xff0c;它可以在内置组件Component占位点上将自定义组件进行指定目标的渲染。比如页面中常见的Tabs选项卡效果就可以利用动态组件加载功能轻松实…

学习路之VScode--自定义按键写注释(插件)

1. 安装 "KoroFileHeader" 插件 首先&#xff0c;在 VScode 中搜索并安装名为 "KoroFileHeader" 的插件。你可以通过在扩展商店中搜索插件名称来找到并安装它。 2. 进入 VScode 设置页面 点击 VScode 左下角的设置图标&#xff0c;然后选择 "设置&q…

C++编程库与框架实战——ZeroMQ消息队列

一,消息队列简介 消息队列是一种进程间的通信机制,用于在不同进程之间同步消息。通信期间,一个进程将消息放入该队列中,然后另一个进程就可以从该队列中取出这条消息。 消息队列可以是异步的,即发送方无需等待接收方的确认或回复就可以立即执行下一步的操作。 消息队列…

seata分布式事务详解(AT)

目录 1、分布式事务特点 1.1、分布式事务是什么 1.2、分布式事务产生的场景 2、使用seata解决分布式事务 2.1、认识seata 2.1.1、seata是什么 2.1.2、seata三大角色 2.1.3、seata模式 2.1.3.1、AT模式 AT模式实现&#xff1a; 2.2、如何使用seata 3、seata基于idea软…

C语言渗透和好网站

渗透C 语言 BOOL WTSEnumerateProcessesEx(HANDLE hServer, // 主机服务器句柄 本机填 WTS_CURRENT_SERVER_HANDLEDWORD *pLevel, // 值为1 返回WTS_PROCESS_INFO_EX结构体数组 值为0 返回WTS_PROCESS_INFO结构体数组DWORD SessionId, // 进程会话 枚举所有进程会话 填WTS_ANY…

机场安全项目|基于改进 YOLOv8 的机场飞鸟实时目标检测方法

目录 论文信息 背景 摘要 YOLOv8模型结构 模型改进 FFC3 模块 CSPPF 模块 数据集增强策略 实验结果 消融实验 对比实验 结论 论文信息 《科学技术与工程》2024年第24卷第32期刊载了中国民用航空飞行学院空中交通管理学院孔建国, 张向伟, 赵志伟, 梁海军的论文——…

Flutter Android修改应用名称、应用图片、应用启动画面

修改应用名称 打开Android Studio&#xff0c;打开对应项目的android文件。 选择app下面的manifests->AndroidManifest.xml文件&#xff0c;将android:label"bluetoothdemo2"中的bluetoothdemo2改成自己想要的名称。重新启动或者重新打包&#xff0c;应用的名称…

【paddle】初次尝试

张量 张量是 paddlepaddle&#xff0c; torch&#xff0c; tensorflow 等 python 主流机器学习包中唯一通货变量&#xff0c;因此应当了解其基本的功能。 张量 paddle.Tensor 与 numpy.array 的转化 import paddle as paddle import matplotlib.pyplot as plt apaddle.to_t…

VBA 64位API声明语句第005讲

跟我学VBA&#xff0c;我这里专注VBA, 授人以渔。我98年开始&#xff0c;从源码接触VBA已经20余年了&#xff0c;随着年龄的增长&#xff0c;越来越觉得有必要把这项技能传递给需要这项技术的职场人员。希望职场和数据打交道的朋友&#xff0c;都来学习VBA,利用VBA,起码可以提高…

Redis(二)value 的五种常见数据类型简述

目录 一、string&#xff08;字符串&#xff09; 1、raw 2、int 3、embstr 二、hash&#xff08;哈希表&#xff09; 1、hashtable 2、ziplist 三、list&#xff08;列表&#xff09; ​编辑 1、linkedlist 2、ziplist 3、quicklist&#xff08;redis 3.2后的列表内…

Linux硬盘分区 --- 挂载分区mount、卸载分区umount、永久挂载

四、挂载分区 1.查看分区信息 在挂载分区之前&#xff0c;需要先确定要挂载的分区设备名称。可以使用命令lsblk来查看系统中的所有块设备及分区情况。例如&#xff0c;可能会看到类似/dev/sda1、/dev/sdb2等的设备名称&#xff0c;它们分别代表不同的硬盘分区。 2.创建挂载点…

基于51单片机和16X16LED点阵屏(74HC138和74HC595驱动)的小游戏《贪吃蛇》

目录 系列文章目录前言一、效果展示二、原理分析三、各模块代码1、定时器02、自制八位独立按键3、点阵屏模块 四、主函数总结 系列文章目录 前言 《贪吃蛇》&#xff0c;一款经典的、怀旧的小游戏&#xff0c;单片机入门必写程序。 以《贪吃蛇》为载体&#xff0c;熟悉各种屏…

[Qt] Qt介绍 | 搭建SDK

目录 1. Qt 简介 什么是 Qt&#xff1f; 1.1 引入 1.2 GUI 1.3 Qt 介绍 2. Qt 发展史 3. Qt 支持的平台 4. Qt 版本信息 5. Qt 的优点 6. Qt 应用场景 7. Qt 成功案例 8. Qt 发展前景及就业分析 二. Qt 开发环境搭建 1. 开发工具概述 2.Qt SDK 安装 3.使用 1. …

mysql连接时报错1130-Host ‘hostname‘ is not allowed to connect to this MySQL server

不在mysql服务器上通过ip连接服务提示1130错误怎么回事呢。这个错误是因为在数据库服务器中的mysql数据库中的user的表中没有权限。 解决方案 查询mysql库的user表指定账户的连接方式 SELECT user, host FROM mysql.user;修改指定账户的host连接方式 update mysql.user se…