目录
1.Introduciton
部分简述:
背景:高性能计算与深度学习快速发展
需求:
- 抽象需求:集合通信的编程模型、实现与其重要性(MPI诞生超过30年)
- 显示需求:超大模型(例如GPT3,参数量可达175billion)训练时间阻碍了模型的快速应用部署。
1.1 集合通信的编程模型:MPI(Message Passing Interface)
MPI因为速度快,可移植性好,被广泛应用。
MPI的实现:MPICH,MVAPICH,OpenMPI
当然文中也提到了非MPI的库:OpenSHMEM, UCX, UCC
下图为其演变过程:包含编程模型、集合通信操作到底层硬件的变迁。
1.2 大模型训练加速:大规模并行集合通信+RDMA等通信机制和硬件
RDMA: Remote Direct Memory Access、GDR:GPU Direct技术、IB网卡等。
RDMA相当于是DMA技术在多机情况下的演变。
在这些技术影响下一些多节点(多机)通信速度可达到400Gbps。
1.3 针对分布式通信库提出问题
如今工业界有NCCL, RCCL, Gloo(如上图所见)。
由此文中提出两个问题:
1)这些工业界的xCCL库相比经典MPI的优势在于?
2)这些库的性能如何?
2.Collective Communication Routines
各类的routines实现如下表:
Table 1. Summary of Collective Support Within Libraries
有些未在本文中进行讨论,有些在xCCL的库中没有实现。
本质上,除非有特殊的优化方法,一些集合通信操作都是可以用组合的方式来实现。
2.1 Broadcast
Broadcast这一集合操作描述了根节点将相同的数据分发给系统内所有节点的过程。
broadcast操作完成后,每个节点上拥有相同的数据D。上图中的p代表进程,t代表时间节点。
2.2 All-Gather
all - gather集合操作的结果:每个节点接收来自系统内所有节点的数据。
All-Gather相当于每个节点都把自己的数据广播给了其他节点。
2.3 Scatter
Scatter集合操作涉及到一个进程根据一定的分割模式或规则向其他进程传输不同的数据。
p0进程上的数据,按照系统内进程的数目分成多份,再分散到各个进程上。所以叫散点操作。
这个操作在特定场合下,相较于broadcast有更好的优势,因为有些时候不同进程需要分发不同的任务,这样更能节省带宽。
2.4 All to All(v)
一般的All-to-All操作 是指每个进程向系统中的其他进程发送数据。
标准All to All :All to Allv,每份数据尺寸相同
非标准的:可以允许分发不同size的数据
操作结束后,得到的结果可以看作是原来的转置。
做数据并行(DP)和模型并行(MP)的时候这个东西很重要。
2.5 Reduce
Reduce集合指的是一个过程,在这个过程中,单个节点从系统中的每个节点接收数据,并对这些数据应用一些操作,从而产生单个输出。
f(…)可以为任意操作。
这个集合操作能允许进程间并行执行某一操作,同时保证确定性和正确性。
2.6 All-Reduce
All-Reduce集合操作可以被描述为一个Reduce步骤后面跟着一个Broadcast步骤。
在该操作结束后,所有进程都会获得reduce操作后的结果。
All-Reduce广泛应用于数据并行(DP)分布式深度学习训练任务中,在反向传播阶段(BP)计算和传递梯度。
2.7 Reduce-Scatter
Reduce-Scatter集合操作,可以被描述为Reduce操作和Scatter操作按给定顺序的组合。
但更确切的描述是,reduce得到的结果会按照进程数被分成n分,再分发给不同的进程。
3 Network Topologies for Collectives
MPI通过通信器构造这种抽象方法,隐藏了进程间复杂的次层次通信细节。
但实际的物理拓扑对性能有很大的影响(特别是在硬件架构有限,而应用的性能本身又被通信所阻塞时)。
3.1 Hypercube
hypercube由一组以多维立方体模式(超立方体)连接的节点组成。
节点增加,则系统中的立方体维度随之增加。
假设维度为k,则一个完全的hypercube拥有2k个节点(node)。
不完全的hypercube拥有任意数量的节点。
如图是一个4D hypercube的例子(16节点)。若是1D,就有两个节点,2D有4个节点。
Hypercube在高性能(HPC)应用中并不常用。
Hypercube的高链接度:优点是不受节点通信失败所影响;缺点是随着节点增多,网络会更复杂,也不好扩展。
3.2 Ring Topo
环形拓扑(Ring)是一种结构,其中的所有成员以概念上的环形方式连接。
每个成员都拥有两个链接。数据块会在两两之间,依次传递,直到抵达终点。
两种传递模式:
- 单向环形网络(unidirectional ring network)
- 双向环形网络(bidirectional ring network)
三个优势:
- 所有数据都在一个方向上流动,减少了冲突(两种模式都是)
- 可以轻松加入新设备且不影响传输速度
- 不需要中央服务器来协调网络运输
三个缺陷:
- 传一个数据要过所有设备,不灵活
- 一个设备失败,整个网络受影响
- 此外,对于短报文,环形拓扑的信道利用率不高,可能出现带宽碎片
3.3 Torus Topo
环面拓扑是环形拓扑(Ring)在高维上的推广
也就是从1D变成2D,ring中每个节点有前后两个链接,torus里面每个节点有4个链接。
假设维度为N,则整个拓扑中每个成员拥有2N个链接。传播的方向有2N个。
优势:
- 减小拓扑的直径(规模),降低添加新成员的成本(只需要加入对应链接就行)
- 更高的带宽、更低延迟(在保持可扩展性的前提下)
限制:
- 维度增加时,链接的数目也会增加
- 新添加的节点,会需要更多能量和通信时间(因为要经过前面的所有节点)
因此,后来又诞生了折叠环面拓扑(folded-torus topology)。
3.4 Fat-Tree Topo
和传统计算机中的树结构不同,胖树拓扑和现实生活中的树更像
Fat-Tree的应用很广泛,例如nccl中的colnet。
如图,所有分支都具有相同厚度(带宽),因此通信带宽更大。越靠近根节点的部位也越胖。
叶子节点用于计算,其他节点用于通信(严格)。
当一个叶子节点A要与其他叶子节点B通信时,数据将递归地向上层流动,直到找到AB的共同祖先,再依次向下流动到B。
优势:
- 节点之间的平均距离呈对数增长,因为是树结构
- 在良好的算法设计下,具备更好的可拓展性和分区性
- 对称性、规律性和高连接度
缺点:
- 所有分支连接到root,树越大,带宽要求越高
- 负载平衡和调度问题(因为两个叶子节点通信需要跨越中间所有节点)
3.5 Dragonfly and Dragonfly+
蜻蜓拓扑[Dragonfly],是由多个层次(即路由器和组)组成的分层结构。
组内:每个节点互联。
组外:每个组之间互联。
一个组内的路由,称为“virtual router”,他的基数为k。
k=a *(p+h)。a是组内成员数目,p+h是每个成员的连接数。
优点:
- 模块化(组内链接和组间链接解耦了)
- 可拓展性(增加有效基数k,在扩大节点数目同时保持相对较低的跳数)
缺点:
- 建构成本高
- 路径太多样,某些情况下会有较低的网络利用率和吞吐量。
为了解决这个问题,出现了dragonfly+拓扑
这一部分详见:Clos 网络/胖树,或者<A scalable, commodity data center network architecture>这篇论文。
4 Collective Communication Algorithms
4.1 Classic Collective Communication Algorithms
上表是算法的最新进展一览,包括各种有代表性的SOTA算法。
HPC工作负载中最常用的:Ring、Binomial Tree、Recursive-Doubling、Recurive-Halving。
这里提出了一个很重要的模型,也是我阅读这篇文章的目的。
这个模型用于评估算法的延迟和带宽,称之为α-β cost model。
参考:<Hockney R W. The communication challenge for MPP:Intel paragon and Meiko CS-2. Parallel Computing, 1994,20(3): 389–398. DOI: 10.1016/S0167-8191(06)80021-9.>
进程间单向通信,耗时:
α
+
n
β
α+nβ
α+nβ
进程间双向通信,耗时:
α
u
n
i
+
n
β
u
n
i
α_{uni}+nβ_{uni}
αuni+nβuni
α: 每条消息的传输延迟
n:传输字节数
β:每字节的传输时间
p:进程数
γ:一个进程中每字节的通信成本
4.1.1 Ring
ring算法常用于All-Gather操作。
All-Gather操作:
第一步:每个进程把自己的这份数据送到下一个进程
第二步:每个进程把自己上一步收到的前一进程的数据传到下一个进程
进程数为p,则完成整个算法需要耗费p-1步
需要gather的数据总量为n,则每一步中,每个进程收到的数据是n/p,因此耗时如下:
T
r
i
n
g
A
l
l
−
g
a
t
h
e
r
=
(
p
−
1
)
α
+
(
(
p
−
1
)
/
p
)
n
β
T_{ring}^{All-gather}=(p-1)α+((p-1)/p)nβ
TringAll−gather=(p−1)α+((p−1)/p)nβ
All-Reduce操作:
这个操作要分为Reduce-Scatter和All-Gather两个阶段。Reduce-Scatter阶段可以在Pairwise-Exchange算法中执行。当进程数不是2的幂时,该算法带宽利用率很高。但算法延迟仍然会随着进程数增加而线性增长。
T r i n g A l l − r e d u c e = 2 ( p − 1 ) α + 2 n β + n γ − ( 1 / p ) ( 2 n β + n γ ) T_{ring}^{All-reduce}=2(p-1)α+2nβ+nγ-(1/p)(2nβ+nγ) TringAll−reduce=2(p−1)α+2nβ+nγ−(1/p)(2nβ+nγ)
4.1.2 Binomial Tree
该算法常用于MPICH中的broadcast。
Broadcast操作:
第一步:进程(root+(p/2))从root收到数据。
第二步:root和进程都在各自的子树中作为新的root来发送数据。
该算法递归运行,耗费
lg
p
\lg p
lgp步。在任意一步中,每个进程都可传输n bytes的数据。
T
t
r
e
e
B
r
o
a
d
c
a
s
t
=
(
lg
p
)
α
+
n
β
T_{tree}^{Broadcast}=(\lg p)α+nβ
TtreeBroadcast=(lgp)α+nβ
由于该算法具有对数延迟项,因此在短消息通信中表现良好。适合情况,如数据量小于12KB, 进程数小于8。
Reduce操作:
同样耗费
lg
p
\lg p
lgp步,
T
t
r
e
e
R
e
d
u
c
e
=
(
lg
p
)
(
α
+
n
β
+
n
γ
)
T_{tree}^{Reduce}=(\lg p)(α+nβ+nγ)
TtreeReduce=(lgp)(α+nβ+nγ)
reduce操作和broadcast一样,在该算法下适用于小数据。
但,有时用户预定义的一些预定操作里,可能改变数据类型,来执行复杂Reduce-Scatter操作,所以All-reduce操作中,该算法适用于所有大小的数据。All-reduce操作一般是先做reduce-scatter,等reduce结果传到rank 0了,再broadcast出去。
4.1.3 Recursive-Doubling
适用于All-Gather的算法。
All-Gather操作:
第一步:每个进程从自己的邻居(相邻进程)那里接受和发出数据
第二步:每个进程从距离自己两个进程远的邻居那里接收和发送数据
第三步:每个进程与距离自己4个进程距离的邻居交换数据…
…
当进场数p是2的幂,所有操作可在
lg
p
\lg p
lgp步内完成。
第一步交换的数据量为:n/p
第二步则是2n/p
最后一步交换数据量为
(
2
lg
p
n
)
/
p
(2^{\lg p}n)/p
(2lgpn)/p
耗时:
T
r
e
c
−
d
b
l
A
l
l
−
G
a
t
h
e
r
=
lg
p
α
+
(
(
p
−
1
)
/
p
)
n
β
T_{rec-dbl}^{All-Gather}=\lg pα+((p-1)/p)nβ
Trec−dblAll−Gather=lgpα+((p−1)/p)nβ
这个操作很适合进程数为2的次方。反之则不适合。
Reduce-Scatter操作:
和上述相似,但数据量更大。
第一步:每个进程所需的结果数据在这一步并不进行交换,每个进程交换的数据量为
n
−
(
n
/
p
)
n-(n/p)
n−(n/p)
第二步:它们自己和在每个过程的前一步中通信的进程所需的数据不进行交换, 每个进程交换的数据量为
n
−
(
2
n
/
p
)
n-(2n/p)
n−(2n/p)
第三步:交换数据量
n
−
(
4
n
/
p
)
n-(4n/p)
n−(4n/p)
耗时:
T
r
e
c
−
d
b
l
R
e
d
u
c
e
−
S
c
a
t
t
e
r
=
lg
p
α
+
(
lg
p
−
(
(
p
−
1
)
/
p
)
)
n
β
+
(
lg
p
−
(
p
−
1
/
p
)
)
n
γ
T_{rec-dbl}^{Reduce-Scatter}=\lg pα+(\lg p- ((p-1)/p))nβ+(\lg p- (p-1/p))nγ
Trec−dblReduce−Scatter=lgpα+(lgp−((p−1)/p))nβ+(lgp−(p−1/p))nγ
该算法适用于简洁的消息(< 32B)
All-Reduce操作:
与all-gather相似。(除非步骤中包含本地reduce)
在长短消息中都表现良好。
耗时:
T
r
e
c
−
d
b
l
A
l
l
−
R
e
d
u
c
e
=
lg
p
α
+
n
lg
p
β
+
n
lg
p
γ
T_{rec-dbl}^{All-Reduce}=\lg pα+n\lg p β+ n\lg p γ
Trec−dblAll−Reduce=lgpα+nlgpβ+nlgpγ
4.1.4 Recursive-Halving
如果节点数是2的幂,所需通信步数是2*log2N。 相比reduce+broadcast,最大的改进是规避了单节点的带宽瓶颈。
All-Gather的执行和上述(Recursive-Doubling)相似
Reduce-Scatter操作:
第一步:相距p/2远的进程之间彼此交换数据(包含发送和接受), 每个进程都要从另一半收或者发。然后对收到的数据进行reduction操作。
第二步:相距p/4远的进程之间彼此交换数据, 每个进程都要从另一部分的子树收或者发。这个过程是递归地执行的,每一步传输的数据减半。
所有操作可在
lg
p
\lg p
lgp步内完成。如果进程数p是2的指数。耗时:
T
r
e
c
−
h
a
l
f
R
e
d
u
c
e
−
S
c
a
t
t
e
r
=
lg
p
α
+
(
(
p
−
1
)
/
p
)
n
β
+
(
(
p
−
1
)
/
p
)
n
γ
T_{rec-half}^{Reduce-Scatter}=\lg pα+((p-1)/p)n β+ ((p-1)/p)n γ
Trec−halfReduce−Scatter=lgpα+((p−1)/p)nβ+((p−1)/p)nγ
[未完待续] Section 4~8 放在下一篇。
点击看第二篇