理解NCCL的Tree

本文深入解析了NCCL中的Tree和Ring概念,重点介绍了节点内的chain结构、节点间Tree的构建,以及如何通过Tree进行Allreduce操作,对比了不同模式下的带宽利用。作者通过实例和代码解释了Tree的构建原理和在Allreduce中的作用。
摘要由CSDN通过智能技术生成

作为NCCL的小白,阅读了NCCL有关Tree的源码,参考过CSDN许多大佬的blog,奈何自己悟性有限,始终参悟不了NCCL的Tree的概念。

结合了github中issue的解答,自己在环境上通过log一步一步把Tree里的关系画出来,发现自己好像明白了一些东西,整理了一下对Tree的理解。

NCCL里Tree的概念其实是节点内的chain和节点间Tree的组合。

Ring的概念也是一样的,节点内的Ring和节点间Ring的组合。

1、Tree的位置

Tree只在节点之间生效,这个说法在任何地方都是这么说的,但是讲的不太清楚。

Tree确实只是在节点之间生效的,但是描述这个Tree的元素并不是节点编号,而是每个节点中的GPU编号(代码中是用NODE编号来组装的双二叉树,但是通过channel的tree.up和tree.down画出来图,却发现其实真正的节点是GPU,查看log:NCCL INFO Trees ),这些GPU编号是全局的。

(1)在每个节点内部,会产生一个描述节点内所有GPU的chain,这个chain很类似于Ring中的环,但是这个chain是个单向的,有头有尾的那种chain。

这个chain可以理解为有一个尾节点,然后一直指向root rank节点,这样每个节点上都有自己的一个root rank节点。这些root rank节点组织成Tree。

(2)ncclTopoPreset的最后有一步复制channel的操作,这个操作其实就是在节点内部产生一样的chain,这样两个一样的chain会组成两颗互补的二叉树,也就说的双二叉树。

下面用issue中假设的场景来画图描述一下Tree的结构

Understand the tree topology · Issue #671 · NVIDIA/nccl · GitHub

上图可以看出来,每一行代表一个节点中的GPU编号,所有GPU编号是全局的。

每一行的最右边的GPU代表了这个节点里的root rank,这些root rank组成了Tree(注意每个节点中的root rank和Tree的root rank不要弄混了,这里我也不好表达,就这么描述吧)

Tree在NCCL中的定义:

// The root of each tree only has one node down (+1 intra-node).
#define NCCL_MAX_TREE_ARITY_TOP 2
// Nodes inside the binary tree can have to two nodes down (+1 intra-node).
#define NCCL_MAX_TREE_ARITY 3
struct ncclTree {
  int depth;
  int up;
  int down[NCCL_MAX_TREE_ARITY];
};

up就代表了当前这个rank的上一个rank,在chain里就是本节点内的上一个rank,在Tree里就是节点间的上一个rank。

down总共有三个元素,为什么二叉树会有三个元素,是因为down[0]代表了节点内chain中本GPU的下一个GPU,而down[1]和down[2]才是在Tree上用的。代表了节点间本GPU的叶子GPU。

设计的挺巧妙的,但是乍一看不好理解。下面图里能看出来,在Tree上的GPU的down都是down[1]和down[2],而down[0]都是在节点内部的chain上用的。

2、三种Tree的对比

NCCL中定义的三种Tree

#define NCCL_TOPO_PATTERN_BALANCED_TREE 1   // Spread NIC traffic between two GPUs (Tree parent + one child on first GPU, second child on second GPU)
#define NCCL_TOPO_PATTERN_SPLIT_TREE 2      // Spread NIC traffic between two GPUs (Tree parent on first GPU, tree children on the second GPU)
#define NCCL_TOPO_PATTERN_TREE 3            // All NIC traffic going to/from the same GPU

其中NCCL_TOPO_PATTERN_TREE 是最简单的,在Tree的组织上也比较容易理解

issue中的解释也不好描述,直接看看图吧,就比较清晰一些了。

依旧以issue中4机32卡的情况画一下Tree的形状

(1)NCCL_TOPO_PATTERN_TREE

在basic Tree中,每个NODE的GPU 0负责做节点内的UP和DOWN操作,比如:0, 16, 8, 24

(2)NCCL_TOPO_PATTERN_SPLIT_TREE

在split tree中,负责节点间UP操作的还是GPU 0,但是负责DOWN操作变成了GPU 1,比如rank 1, 17 。。。

(3)NCCL_TOPO_PATTERN_BALANCED_TREE

在balance tree中,负责节点间UP操作的还是GPU 0,但是负责DOWN操作变成了GPU 0和GPU 1,分别负责向Tree中两个子节点做DOWN

3、单tree和双Tree的对比

为啥说单Tree会造成带宽的浪费,而使用双Tree就可以把双向带宽打满呢?

我个人分析了一下,自己的的理解不一定正确。参考issue中的讨论。

Question: NCCL Tree algorithm behaviour · Issue #919 · NVIDIA/nccl · GitHub

这个问题的理解其实比较微妙,可以固定在一次数据传递过程中进行分析。

(貌似奇数个RANK和偶数个RANK的结论稍微有点不一样,奇数个RANK貌似更能充分利用双向带宽,下面的解析是用偶数个RANK进行分析的)

             

                                              Tree 1                

                                                                              Tree 2

看上图分析

前面分析了在内核里通过Tree上不同的节点有不同的操作,完成数据的传递,需要Reduce+Broadcast两个步骤。下面就传递完一块数据的内部实现来分析。

Reduce:从Tree的叶子节点开始向上进行reduce,叶子节点只发不收,只使用了发送带宽,而没有用到接收带宽,中间节点肯定是即收又发,所以是使用了双向带宽,到了Tree的root节点,只收不发,只使用了接收带宽。

Broadcast:在来看Broadcast阶段,从Tree的root节点向下进行Broadcast,root节点只发不收,只使用了发送带宽,中间节点肯定是即收又发,所以是使用了双向带宽,到了叶子节点只收不发,只使用了接收带宽。

只使用Tree 1的情形:

所有的数据都通过Tree 1进行Reduce+Broadcast操作

Reduce过程:4,12,20,28只使用了发送带宽,8,16,24使用了发送带宽和接收带宽,0只使用了接收带宽。

Broadcast过程:0只使用了发送带宽,8,16,24使用了发送带宽和接收带宽,4,12,20,28只使用了接收带宽。

NodereducereduceBroadcast
01111
41111
8111122
121111
16111122
201111
24111122
281111

可见root节点和叶子没有充分使用全部的发送接收带宽,正好这些节点占一半,所以就相当于浪费了一半带宽

使用两个Tree的情形:

一半数据通过Tree 1进行Reduce+Broadcast操作,另一半数据通过Tree 2进行Reduce+Broadcast操作

Tree 1中:

Reduce过程:4,12,20,28只使用了发送带宽,4,12,20使用了发送带宽和接收带宽,0只使用了接收带宽。

Broadcast过程:0只使用了发送带宽,4,12,20使用了发送带宽和接收带宽,4,12,20,28只使用了接收带宽。

Tree 2中:

Reduce过程:0,8,16,24只使用了发送带宽,8,16,24使用了发送带宽和接收带宽,28只使用了接收带宽。

Broadcast过程:28只使用了发送带宽,8,16,24使用了发送带宽和接收带宽,0,8,16,24只使用了接收带宽。

Node

Tree1

Tree2

reducereduceBroadcastBroadcastreducereduceBroadcastBroadcast
0111122
411111133
811111133
1211111133
1611111133
2011111133
2411111133
28111122

把完整的数据分成两半,每个tree处理一半,则从上图可以看出,两个Tree的root节点只收发了一倍的完整数据量,其他节点都收发了1.5倍的完整数据量。

在一次数据传递过程中,除了两个root节点,其他的所有节点都完全使用了双向带宽。

是这样的吗?

还是说全部节点都没有带宽浪费呢?

4、通过Tree进行Allreduce的过程

因为NCCL里只对Allreduce实现了Tree算法,Tree的Allreduce是分成了两块,一块是做reduce,一块是做Broadcast,跟Ring算法不太一样。

reduce阶段:从每个节点的每个chain的末尾那个chain开始向上reduce,一直reduce到每个节点 的root rank,root rank继续向上reduce,一直reduce到整个Tree的root rank

Broadcast阶段:从整个Tree的root rank向下做Broadcast,每个节点的root rank也在节点内顺着chain向下做Broadcast

5、Tree的建立

包括B-Tree的算法,没有仔细的研究,算法功底不行。。。。

(后续补充)

6、参考

https://developer.nvidia.com/blog/massively-scale-deep-learning-training-nccl-2-4/

https://github.com/NVIDIA/nccl/issues/919

https://github.com/NVIDIA/nccl/issues/545

Understand the tree topology · Issue #671 · NVIDIA/nccl · GitHub

评论 18
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值