Distributed Training Collective Communication
集合通信
作为并行计算的一个重要概念,集合通信经常被用来构建高性能的单程序流/多数据流(Single Program-Multiple Data, SPMD)程序。
接下来主要介绍如何使用 Allreduce 算法解决分布式训练系统的瓶颈,并且讨论 Allreduce 算法在不同网络拓扑下的差异和评价标准。
常见通信算子
- Broadcast
- Reduce
- AllReduce
- Gather
- AllGather
- Scatter
在分布式机器学习训练中,还有其他的集合算子,如ReduceScatter、Prefix Sum、Barrier、All-to-All等。
通信模型
在一个分布式机器学习的网络中,有 p 个计算设备,并有一个网络连接,每个设备都有独立内存,设备间通信通过点对点(P2P)网络通信完成。
-
每次通信只有一个发送者和接受者。一个设备某个时刻只能接收或发送一个消息。
-
传输一个 l 长的字节的消息需要花费
a+b*l
的时间-
a 表示网络延时,这个值与网络的物理距离(如网线长度)有关,是发送消息时的固定延迟,不随消息长度变化。
-
b 表示每个字节的传输延时,这个值与网络带宽有关。
传输延时是指发送方将消息的每个字节传输到网络介质上所需的时间。它通常与网络带宽相关联,表示数据从发送端开始传输到完全进入网络所需要的时间。
-
l 表示消息的长度,以字节为单位。
-
算子详解
Broadcast
设有 p 个设备,正常来说要从一个设备传输数据到其他 p-1 个设备,时间复杂度为 p-1
更好的办法:分治思想,时间复杂度为 logp
- 第1次传输,设备 1 传输数据到第 p/2 的设备,接下来设备1 负责传输数据到
1 ~ p/2-1
的设备,而第 p/2 的设备负责传输数据到p/2 ~ p
的设备 - 第2次传输,设备 1 传输数据到第 p/4 的设备,设备 p/2 传给第 3/4 的设备。再继续划分责权到目前有数据的四个设备上
- 。。
- 第 n-1 次传输, p/2 的设备各自传输到另外 p/2 的设备上
- 第 n 次传输,每个设备 i 都需要传输数据到
[i,i]
这个范围上的设备,达成临界条件,返回。
Reduce
Reduce 算子要做的事情是把 p 个设备的数据进行聚合(Aggregation),聚合操作符合结合律(Associative Law)和交换律(Commutative Law),常见有加和、乘积、最大值和最小值函数。
设有 p 个设备,正常来说要从p-1个设备传输数据到1个设备,时间复杂度为 p-1
同样可以采用分治思想,时间复杂度为 logp
dp(target) = dp(1, n)
dp(1, n) = f(dp(1, p/2-1) + dp(p/2, p))
AllRecuce
集合通信通过引入AllReduce算子,从而将Reduce函数𝑓的结果存至所有设备上。
结合 Reduce 和 Broadcast 算子,时间复杂度为 logp
- 用 Reduce 算子把数据聚合到设备 1 上
- 用 Broadcast 算子把数据从设备 1 传输到其他所有设备
Gather
Gather算子可以将全部设备的数据全部收集(Gather)到编号为𝑖的设备上。
在收集函数(Gather Function)符合结合律和交换律的情况下,可以把 Reduce 算子的函数 f 改成 gather 函数实现。
但由于相对于每个单机上原有的数据规模 l,第 t 次传递的数据规模要乘以 2^t
所以时间复杂度为 a*logp + (p-1)*b*l
推导如下
a*logp + (1+2^1+...+2^logp)*b*l
a*logp + (1-2^logp)/(1-2)*b*l
a*logp + (1-p)/(1-2)*b*l
a*logp + (p-1)*b*l
AllGather
形同 Gather 算子,不过其在 Gather 完之后需要把数据 Broadcast 到所有算子上
其时间复杂度为 Gather + Broadcast,时间复杂度为
a*logp + (p-1)*b*l
+a*logp +logp*p*b*l
因为在广播时,如果忽略链表/数组实现所带来的额外空间开销,每次通信的长度为p*l
而不是;
。- 简化为
a*logp+p*l*b*logp
- 基于超立方体的算法还能优化成和 Gather 算子一样的复杂度
a*logp + (p-1)*b*l
Scatter
Scatter 算子可以看成是 Gather 算子的逆运算,把一个存在于设备 i 上的长度为 p 的数据(信息长度为 p*l
)的值分散到每个设备上。
和 Gather 类似,不过 Scatter 的分治是逐渐拆分数据,每个阶段 t 的数据大小为 l*2^(m-t)
,其中 m 是当前算法所运行的步骤。
算法复杂度也和 Gather 类似,为 a*logp + (p-1)*b*l
在机器学习系统中,Scatter算子经常同时被用于链式数据结构和可切分的数据结构,例如张量在一个维度上的p等分等。
基于AllReduce的梯度平均算法
可以考虑通过 AllReduce 算子,结合 Reduce 和 Broadcast 算子,时间复杂度为 logp
。
但这种简单的方法存在如下问题:
- 在 Reduce 算子中负责收集所有数据的设备的算力有限,不能够高效计算出所有的平均梯度。
为了解决这个问题,可以引入AllReduce算子的Reduce-Broadcast实现来优化算法。
AllReduce算子会把梯度的计算拆分成 M-1
个Reduce算子和 M-1
个Broadcast算子(其中𝑀是节点的数量)。其中,Reduce算子用于计算出梯度的加和,Broadcast算子用于把梯度之和广播给全部的节点。
Reduce算子对梯度的分布式计算实现了以下的性能优化:
- 网络优化: 全部设备都同时在接收和发送数据,利用起了每个设备的入口(Ingress)和出口(Egress)带宽。因此在AllReduce算法的过程中,可利用的带宽是
M*B
,其中M
是节点数量,B
是节点带宽,从而让系统实现网络带宽上的可扩展性。 - 算力优化: 全部设备的处理器都参与了梯度相加的计算。因此在AllReduce算法的过程中,可利用的处理器是
M*P
,其中M
是节点数量,P
是单个设备的处理器数量,从而让系统实现计算上的可扩展性。 - 负载均衡: 由于数据分区是平均划分的,因此每次设备分摊到的通信和计算开销是相等的。
上图是AllReduce的其中一种常用实现方法。根据集群网络拓扑的不同,人们也会用以下的方法来实现AllReduce:树形结构,环形结构,二维环面结构以及CollNet。在此我们不展开讨论。