简介
单个GPU的显存上限,限制了训练时的参数规模及batch size,导致训练效率无法达到预期。分布式训练的本质是分布式计算,分布式计算跟着任务切分、存算分离、数据加速、分布式通信等一系列事儿。
关于深度学习框架的一些自己见解我觉得做深度学习框架其实有两个派别的人,一派是从分布式系统的人来做的,另外一派是从做算法的人来做的。不同的人的背景不同,所以做这个事情的角度也会不同,从而产生不同门派。tensorflow属于系统派,而pytorch属于算法派。
- TensorFlow从设计之初就在考虑超大的模型分布式训练的场景,设想这个框架需要能够从规模上很好支持分布式,能够很好的扩展到任意大的深度模型的框架。很自然会把模型的开发过程分成构图和执行两个阶段。构图的时候只是生成一个逻辑执行计划,然后通过显式方式的提交(或者execute),系统再根据用户指定的或者系统智能的决定的placement进行分图,并在这些分图中添加合适的Send-Recv的Op从而构成一个分布式的执行计划。但是这样的设计理念也会带来一些困恼,我们在模型训练时候有时候有些类似控制图的部分,在这种设计理念下,我们必须要把这些控制流图的代码也op化,然后把这些op也整体串联在Tensor的Flow执行图中。但是这种方式会使得一些习惯单机开发的研究人员觉得比较晦涩。
- 框架的另外一派是算法派,特别是感知类模型(图像,语音,语言类)训练,因为这类训练一般都是同步训练,且是数据并行,这样我们就可以利用MPI的AllReduce的通讯源语来进行梯度的汇集计算。因为面向是数据并行的场景,这样话在神经网络部分其实都是单机程序,从而可以利用任何python的语法糖去构建任何的动态的训练控制逻辑(大家也把这种称作动态图)。对于算法研究人员来讲,这种方式写代码比较随性也方便debug,所以在研究界pytorch得到大量的关注和使用。
利用多 GPU 加速深度学习模型训练 从GPU 说到 horovod,很不错的文章
基本理念
分布式TensorFlow入门教程 https://zhuanlan.zhihu.com/p/35083779
炼丹师的工程修养之四: TensorFlow的分布式训练和K8S无论是TensorFlow还是其他的几种机器学习框架,分布式训练的基本原理是相同的。大致可以从以下五个不同的角度来分类。
- 并行模式(任务切分), 对于机器学习的训练任务,原来的“大”问题主要表现在两个方面。
- 一是模型太大,我们需要把模型“拆”成多个小模型分布到不同的Worker机器上;
- 二是数据太大,我们需要把数据“拆”成多个更小的数据分布到不同Worker上。
- 架构模式,通过模型并行或数据并行解决了“大问题”的可行性,接下来考虑“正确性”。以数据并行为例,当集群中的每台机器只看到1/N的数据的时候,我们需要一种机制在多个机器之间同步信息(梯度),来保证分布式训练的效果与非分布式是一致的(N * 1/N == N)。相对成熟的做法主要有基于参数服务器(ParameterServer)和基于规约(Reduce)两种模式。Tensorflow 既有 PS 模式又有对等模式,PyTorch 以支持对等模式为主,而 MXNET 以支持 KVStore 和 PS-Lite 的 PS 模式为主。 快手八卦 — 机器学习分布式训练新思路(1)
- 同步范式(参数更新方式), 在梯度同步时还要考虑“木桶”效应,即集群中的某些Worker比其他的更慢的时候,导致计算快的Worker需要等待慢的Worker,整个集群的速度上限受限于最慢机器的速度。因此梯度的更新一般有同步(Sync)、异步(Async)和混合三种范式。
- 同步模式中,在每一次迭代过程中,所有工作节点都需要进行通信,并且下一步迭代必须等待当前迭代的通信完成才能开始。确保所有的设备都是采用相同的模型参数来训练,需要各个设备的计算能力要均衡,而且要求集群的通信也要均衡。
- 反之,异步式分布算法 则不需要等待时间:当某个节点完成计算后就可直接传递本地梯度,进行模型更新。
- 物理架构,这里主要指基于GPU的部署架构,基本上分为两种:单机多卡和多机多卡
- 通信技术,要讨论分布式条件下多进程、多Worker间如何通信,分为以分为 Point-to-point communication 和 Collective communication 两类,Collective communication常见的技术有MPI,NCCL,GRPC,RDMA等
并行模式
- 模型并行(model parallelism),从不同视角看
- 网络拆分为层:神经网络模型通常都是多层神经元的组合,如果要采用模型并行策略,一般需要将不同的层运行在不同的设备上,但是实际上层与层之间的运行是存在约束的:前向运算时,后面的层需要等待前面层的输出作为输入,而在反向传播时,前面的层又要受限于后面层的计算结果。所以除非模型本身很大,一般不会采用模型并行,因为模型层与层之间存在串行逻辑。但是如果模型本身存在一些可以并行的单元,那么也是可以利用模型并行来提升训练速度,比如GoogLeNet的Inception模块。
- 计算图拆分为子图: 就是把计算图分成多个最小依赖子图,然后放置到不同的机器上,同时在上游子图和下游子图之间自动插入数据发送和数据接收节点,并做好网络拓扑结构的配置,各个机器上的子图通过进程间通信实现互联。
- 单纯的把模型的参数切分到多个GPU上,在使用时通过数据驱动的方式,每个GPU从其他GPU上拉取需要的那部分。比如大embedding参数如果有100GB,可以切分成8份放在8个GPU上。每个minibatch计算时embedding层仅需要gather 100GB中很少的一部分。
- TensorFlow可以说是支持MP的典型框架,通过将device placement暴露给用户,开放了几乎所有的玩法。但是弊端就是大部分用户其实并不能合理的使用,反而是经常因为配置错误导致性能急剧下降。
- 一个比较大的问题是GPU利用率低。一条链上只有一个 GPU 在干活,剩下的都在干等,当没有计算到某个GPU上的模型分片时,这个GPU常常是闲着的。pipeline parallelism一定程度上解决了这个问题,可以把一个 batch 分为若干个 mini-batch,每个节点每次只处理一个 mini-batch 的数据,并且只有当整个批次都训练完成后才会进行参数更新。
- 数据并行:因为训练费时的一个重要原因是训练数据量很大。数据并行就是在很多设备上放置相同的模型,并且各个设备采用不同的训练样本对模型训练。训练深度学习模型常采用的是batch SGD方法,采用数据并行,可以每个设备都训练不同的batch,在反向传播时,为了能将结果共享——确保整个模型参数能够在不同的GPU之间进行同步,所有的梯度都将进行全局归纳。所有worker共享ps 上的模型参数,并按照相同拓扑结构的数据流图进行计算。
- 几乎所有的训练框架都支持这种方法,早期比较流行的开源实现是horovod。现在pytorch ddp和tensorflow mirror strategy都原生的支持了。
- 当batch size=1时,传统方法无法继续切分。
- 对于parameter, optimizer state等batch size无关的空间开销是无能为力的。每个GPU上依然需要一份完整的parameter等副本。
- 流水并行,本质是解决模型并行(模型较后部分的计算必须等前面计算完成)后 效率低的问题 所有人都能用的超大规模模型训练工具
代码示例
数据并行可以直接使用pytroch DataParallel或DistributedDataParallel,模型并行示例代码
import torch
import torch.nn as nn
class SimpleModel(nn.Module):
def __init__(self):
super(SimpleModel,self).__init__()
self.layer1 == nn.Linear(10,20).to('cuda:0')
self.layer2 == nn.Linear(20,1).to('cuda:1')
def forward(self,x):
# 第一层在0号卡上的运行
x1 = x.to('cuda:0')
x1 = self.layer1(x1)
# 将第一层输出移动到1号卡,并在1号卡上执行第二层计算
x2 = x1.to('cuda:1')
x2 = self.layer2(x2)
return x2
model = SimpleModel() # 创建模型示例
input_data = torch.randn(64,10)
output_data = model(input_data)
print(output_data,shape)
张量并行
import torch
import torch.nn as nn
class TensorParallelModel(nn.Module):
def __init__(self,input_size,output_size):
super(TensorParallelModel,self).__init__()
self.input_size = input_size
self.output_size = output_size
self.linear == nn.Linear(input_size,output_size).to('cuda:0')
def forward(self,x):
# 将输入数据切分为两个子张量,分别将x1、x2置于不同的设备上
split_size = x.shape[0] // 2
x1,x2 = x[:split_size].to('cuda:0'), x[split_size:].to('cuda:1')
# 将模型权重和偏差复制到第二个设备
linear2 = self.linear.to('cuda:1')
# 在两个设备上并行计算线性层
y1 = self.linear(x1)
y2 = linear2(x2)
# 合并计算结果并返回
y = torch.cat([y1.to('cuda:1'),y2],dim=0)
return y
model = TensorParallelModel(10,20) # 创建模型示例
input_data = torch.randn(64,10)
output_data = model(input_data)
print(output_data,shape)
框架
阿里云机器学习平台大模型训练框架 EPLEPL(EasyParallelLibrary) 是一个统一多种并行策略、易用的分布式深度学习训练框架,它将不同的并行策略进行了统一抽象。在一套分布式训练框架中,支持多种并行策略,包括数据并行、流水并行和算子拆分并行,并支持不同策略的组合和嵌套使用。同时 EPL 提供了灵活应用的接口,用户只需要添加几行代码就可以实现丰富的并行化策略。模型侧不需要去做任何的代码改动。除了用户手动标记并行策略之外,EPL 也支持自动的并行策略探索,包括自动的算子拆分策略以及流水并行中的自动 layer 切分策略等等。EPL 在框架层面也提供了全方位的优化,包括多维度的显存优化、计算优化、通信优化等,从而实现高效的分布式训练性能。
巨型AI模型背后的分布式训练技术(二)DP,TP,PP,Sharding, Offload,这么多的分布式优化技术,需要怎么用?
Galvatron项目原作解读:大模型分布式训练神器,一键实现高效自动并行稠密大模型拥有着动辄数十亿、百亿甚至万亿规模的参数量,面临高昂的计算、存储、以及通信成本,为 AI 基础设施带来了巨大的挑战。人们研发了很多工具(如 Megatron、DeepSpeed、FairSeq 等)来实现如数据并行、张量模型并行、流水并行、分片数据并行等各种并行范式。但这种粗粒度的封装逐渐难以满足用户对系统效率和可用性的需要。如何通过系统化、自动化的方式实现大模型分布式训练,已经成为了当前 MLSys 领域最为重要的问题之一。最近已经有一些系统开始提及“自动并行”的概念,但它们大部分都还停留在对 API 和算子进行工程上的封装,仍然依赖用户人工反复尝试或系统专家经验才能完成部署,并没有从根本上解决自动并行难题。近日,北大河图团队提出了一套面向大模型的自动并行分布式训练系统 Galvatron,相比于现有工作在多样性、复杂性、实用性方面均具有显著优势,性能显著优于现有解决方案。
通信技术
训练框架面临的是 单机CPU与GPU 之间、单机多GPU之间、多机CPU 之间、多机GPU 之间的通信问题,有各种优化方案,但要对上层提供统一通信接口,并进一步结合机器学习的特点提供 collective Communication 接口。
硬件和协议层
海思专家如何看待RDMA技术? 比较好的一篇文章
DMA全称为Direct Memory Access,即直接内存访问。是一种外设绕开CPU独立直接访问内存的机制。
CPU的最主要工作是计算,而不是进行数据复制。可以看到总线上又挂了一个DMA控制器,它是专门用来读写内存的设备。有了它以后,当我们的网卡想要从内存中拷贝数据时,除了一些必要的控制命令外,整个数据复制过程都是由DMA控制器完成的。过程跟CPU复制是一样的,只不过这次是把内存中的数据通过总线复制到DMA控制器内部的寄存器中,再复制到I/O设备的存储空间中。CPU除了关注一下这个过程的开始和结束以外,其他时间可以去做其他事情。DMA控制器一般是和I/O设备在一起的,也就是说一块网卡中既有负责数据收发的模块,也有DMA模块。
rdma 即remote dma。同样是把本端内存中的一段数据,复制到对端内存中,在使用了RDMA技术时,两端的CPU几乎不用参与数据传输过程(只参与控制面)。本端的网卡直接从内存的用户空间DMA拷贝数据到内部存储空间,然后硬件进行各层报文的组装后,通过物理链路发送到对端网卡。对端的RDMA网卡收到数据后,剥离各层报文头和校验码,通过DMA将数据直接拷贝到用户空间内存中。PS:RDMA 网卡一般很贵。
RDMA本身指的是一种技术,具体协议层面,包含Infiniband(IB),RDMA over Converged Ethernet(RoCE)和internet Wide Area RDMA Protocol(iWARP)。三种协议都符合RDMA标准,使用相同的上层接口,在不同层次上有一些差别。上述几种协议都需要专门的硬件(网卡)支持。
- Infiniband规定了一整套完整的链路层到传输层(非传统OSI七层模型的传输层,而是位于其之上)规范,但是其无法兼容现有以太网,除了需要支持IB的网卡之外,企业如果想部署的话还要重新购买配套的交换设备。
- RoCE从英文全称就可以看出它是基于以太网链路层的协议,v1版本网络层仍然使用了IB规范,而v2使用了UDP+IP作为网络层,使得数据包也可以被路由。
- iWARP协议是IETF基于TCP提出的
tcp/ip | rdma | |
---|---|---|
硬件 | 以太网卡 | RDMA 网卡 |
驱动 | rdma-core | |
接口 | socket | libibverbs |
系列解读SMC-R:透明无感提升云上TCP应用网络性能Shared Memory Communication over RDMA (SMC-R) 是一种基于 RDMA 技术、兼容 socket 接口的内核网络协议,由 IBM 提出并在 2017 年贡献至 Linux 内核。SMC-R 能够帮助 TCP 网络应用程序透明使用 RDMA,获得高带宽、低时延的网络通信服务。
基于阿里云 eRDMA 的 GPU 实例如何大幅提升多机训练性能eRDMA 技术由阿里云在 2021 年云栖大会发布,与 RDMA 生态完全兼容。
- 在服务器节点间的数据通信过程中,CPU 仅负责指定 RDMA 通信的源及目的物理地址,并触发通信动作,其余工作全部由物理网卡上的 DMA 引擎负责完成。相比传统的 TCP 通信,近端及远端的物理网卡通过协作可以直接将本地的内存数据写入远端的内存地址空间,或从远端的物理内存空间读取数据到本地内存,待数据传输全部完成后,再通知 CPU 进行下一步动作,将数据传输和计算分开,从而实现高效的并行计算处理。
- DMA 引擎同样可以对设备地址进行直接访问。在异构计算的应用场景中,服务器节点间同步的不仅是内存的数据,还有 GPU 显存的数据。GDR 便是为了实现物理绑卡在服务节点间直接搬移 GPU 显存的数据而实现。为了支持该功能,NVIDIA 的 GPU 驱动提供了一套标准接口,用于获取应用层申请的显存对应的物理地址。物理网卡可以使用这一物理地址完成 DMA 的直接访问。
- eRDMA 基于阿里云的 VPC 网络,因此不论是纯粹的 CPU 服务器还 GPU 服务器,均可以通过添加 eRDMA 设备激活 eRDMA 功能。在神龙底层的实现上,它由神龙 CIPU 模拟出 VirtIO 网卡设备和 ERI 设备,通过神龙的虚拟化层分配给弹性服务器。在用户视角有两个设备,分别为 VirtIO 和 ERI,底层物理设备的访问对用户完全透明,用户仅需要安装阿里云提供的 eRDMA 驱动即可使用。
GPU 卡间通信
AI大模型时代的RDMA网络杂谈GPU机内通讯技术:PCIe是机内通讯最重要的组件之一,它采用的是树状拓扑结构,在这个体系结构中,CPU和内存是核心,GPU、NIC、NVMe等都是外设 ,作为辅助,如下图所示。
然而,在深度学习时代,这一范式改变了,GPU成为了计算的核心,CPU反而只扮演的控制的角色,如下图所示。
在机器内部的GPU-GPU之间,如果通讯仍然走PCIe/QPI/UPI等时,那往往会成为瓶颈;因此,NVIDIA专门提出了NVLink、NVSwitch等新的机内通讯元件,可以为同一机器的GPU之间提供几百Gbps甚至上Tbps的互联带宽。在机器之间,GPU间的通讯需要经过NIC,在没有PCIe Switch的情况下,GPU-NIC之间的通讯需要经过RC,并且会使用CPU做一次拷贝和中转,往往会成为瓶颈;为此,NVIDIA又搞出了GPU Direct RDMA(GDR)技术,让GPU的数据可以直接DMA到网卡上,而不需要经过CPU的拷贝和中转。
那么,一个自然的问题就是,如何判断GPU之间是的连接方式呢?NVIDIA当然想得非常周到了,提供了nvidia-smi topo -m
的命令,可以查看机内的连接方式。然而,值得注意的是,并非所有机器都会组装NVLink、NVSwitch、PCIe Switch等,毕竟这都是成本。所以,在给定机型下的GPU通讯性能最优、到底开不开GDR、PCIe参数到底怎么设置都需要根据具体机型和具体通信模式而具体分析了。最好的方式还是在购买GPU服务器和搭建物理网络时,就结合模型特点和实现方式,设计好GPU服务器的GPU-GPU、GPU-NIC等机内互联和NIC-交换机-NIC的网络互联,这样才能不至于在任何一个地方过早出现瓶颈,导致昂贵GPU算力资源的浪费。
大模型要利用分布式的GPU算力,通讯库是关键环节之一。通讯库向上提供API供训练框架调用,向下连接机内机间的GPU以完成模型参数的高效传输。目前业界应用最为广泛的是NVIDIA提供的NCCL开源通讯库,各个大厂基本都基于NCCL或NCCL的改造版本作为GPU通讯的底座。NCCL是一个专门为多GPU之间提供集合通讯的通讯库,或者说是一个多GPU卡通讯的框架 ,它具有一定程度拓扑感知的能力,提供了包括AllReduce、Broadcast、Reduce、AllGather、ReduceScatter等集合通讯API,也支持用户去使用ncclSend()、ncclRecv()来实现各种复杂的点对点通讯,如One-to-all、All-to-one、All-to-all等,在绝大多数情况下都可以通过服务器内的PCIe、NVLink、NVSwitch等和服务器间的RoCEv2、IB、TCP网络实现高带宽和低延迟。
深度学习分布式训练框架 horovod (3) — Horovodrun背后做了什么 Collective communication包含多个sender和多个receiver(相对于P2P 模式只有一个sender和一个receiver),一般的通信原语包括 broadcast,All-to-one (gather),all-gather,One-to-all (scatter),reduce,all-reduce,reduce-scatter,all-to-all等。集合通信库的主要特征是:大体上会遵照 MPI 提供的接口规定,实现了包括点对点通信(SEND,RECV等),集合通信( REDUCE,BROADCAST,ALLREDUCE等)等相关接口,然后根据自己硬件或者是系统的需要,在底层实现上进行了相应的改动,保证接口的稳定和性能。
通信库NCCL
The NVIDIA Collective Communication Library (NCCL) implements multi-GPU and multi-node communication primitives optimized for NVIDIA GPUs and Networking. NCCL provides routines such as all-gather, all-reduce, broadcast, reduce, reduce-scatter as well as point-to-point send and receive that are optimized to achieve high bandwidth and low latency over PCIe and NVLink high-speed interconnects within a node and over NVIDIA Mellanox Network across nodes. Point-to-point communicationOne-to-all (scatter) ,All-to-one (gather) , All-to-all 都可以基于 ncclSend 和 ncclRecv 来实现。
为什么需要NCCL?或者说NCCL 都看了那些活儿?
- communication primitive
- Point-to-point communication,只有一个sender和一个receiver
- Collective communication,包含多个sender多个receiver,一般的通信原语包括broadcast,gather,all-gather,scatter,reduce,all-reduce,reduce-scatter,all-to-all等。
- ring-base collectives,传统Collective communication假设通信节点组成的topology是一颗fat tree,但实际的通信topology可能比较复杂,并不是一个fat tree。因此一般用ring-based Collective communication。将所有的通信节点通过首尾连接形成一个单向环,数据在环上依次传输。以broadcast为例, 假设有4个GPU,GPU0为sender将信息发送给剩下的GPU
- 按照环的方式依次传输,GPU0–>GPU1–>GPU2–>GPU3,若数据量为N,带宽为B,整个传输时间为
(K-1)N/B
。时间随着节点数线性增长,不是很高效。 - 把要传输的数据分成S份,每次只传N/S的数据量,GPU1接收到GPU0的一份数据后,也接着传到环的下个节点,这样以此类推,最后花的时间为
S*(N/S/B) + (k-2)*(N/S/B) = N(S+K-2)/(SB) --> N/B
,条件是S远大于K,即数据的份数大于节点数,这个很容易满足。所以通信时间不随节点数的增加而增加,只和数据总量以及带宽有关。 - 那么在以GPU为通信节点的场景下,怎么构建通信环呢?
- 按照环的方式依次传输,GPU0–>GPU1–>GPU2–>GPU3,若数据量为N,带宽为B,整个传输时间为
- NCCL在单机多卡上以及多机多卡实现:单机内多卡通过PCIe以及CPU socket通信,多机通过InfiniBand通信。
// nccl/src/nccl.h.in
ncclResult_t ncclGroupStart();
ncclResult_t ncclGroupEnd();
// peer to peer
ncclResult_t ncclSend(const void* sendbuff, size_t count, ncclDataType_t datatype, int peer,ncclComm_t comm, cudaStream_t stream);
ncclResult_t ncclRecv(void* recvbuff, size_t count, ncclDataType_t datatype, int peer,ncclComm_t comm, cudaStream_t stream);
// Collective Communication
ncclResult_t ncclAllGather(const void* sendbuff, void* recvbuff, size_t sendcount,ncclDataType_t datatype, ncclComm_t comm, cudaStream_t stream);
ncclResult_t ncclReduceScatter(const void* sendbuff, void* recvbuff,size_t recvcount, ncclDataType_t datatype, ncclRedOp_t op, ncclComm_t comm,cudaStream_t stream);
...
// 初始化
ncclResult_t ncclCommInitAll(ncclComm_t* comm, int ndev, const int* devlist);
struct ncclComm {
struct ncclChannel channels[MAXCHANNELS];
...
// Bitmasks for ncclTransportP2pSetup
int connect;
uint32_t* connectSend;
uint32_t* connectRecv;
int rank; // my rank in the communicator
int nRanks; // number of GPUs in communicator
int cudaDev; // my cuda device index
int64_t busId; // my PCI bus ID in int format
int node;
int nNodes;
int localRanks;
// Intra-process sync
int intraRank;
int intraRanks;
int* intraBarrier;
int intraPhase;
....
};
其它
- NCCL 最初只支持单机多 GPU 通信,从 NCCL2 开始支持多机多 GPU 通信。
- Gloo是facebook开源的用于机器学习任务中的集合通信库. It comes with a number of collective algorithms useful for machine learning applications. These include a barrier, broadcast, and allreduce. Gloo 为CPU和GPU提供了集合通信程序的优化实现。但如果是在使用NVIDIA-硬件的情况下,主流的选择是NVIDIA自家的NCCL。
- 利用多 GPU 加速深度学习模型训练多机软件设计一般采用 MPI(Message Passing Interface)实现数据交互。MPI 是一种消息传递库接口描述标准,规定了点对点消息传递、协作通信、组和通讯员概念、进程拓扑、环境管理等各项内容,支持 C 和 Fortran 语言。NCCL 出现得更晚一些,参考并兼容了 MPI 已有 API。NCCL 更多考虑了 GPU 的特性,例如任意两块 GPU 之间的通信开销是有区别的,跨 QPI 情况与同一 PCIe Switch 情况,以及有 NVLink/ 无 NVLink 情况就有明显差异,但 MPI 认为两种情况下 GPU 与 GPU 都是等同的,甚至 MPI 认为跨机器的 GPU 也是等同的,这对于多 GPU 通信效率会有影响。MPI 可以和 NCCL 结合,实现层次化的并行通信机制,即同一台机器上的不同 GPU 之间采用 NCCL 通信,而不同机器上的 GPU 之间采用 MPI 辅助通信。NCCL and MPI
优化手段
关于深度的分布式训练,主要工作主从技术栈上呈现从浅层到深层的一个过程。
- 前三类的优化基本上是处于框架层,需要平台为用户提供基础的框架支持。比如说在计算图的并行化策略方面,我们通过GSPMD和GPipe提供了包括数据并行、模型并行和流水线并行的通用化的并行化策略的抽象层。此外,我们通过DeepSpeed来做支持,用ZeRO (Zero Redundancy Optimizer)来优化Optimizer的显存使用,以及我们可以用低精度的压缩来加速参数的同步
- 集合通信层的一些优化,这类优化对于用户跟上层的框架完全透明,不需要改动上层的代码就能够真正落地。拿网络的协议站做一个类比的话,NCCL基本上跟IP协议一样,是整个协议栈的narrow waist的位置。Fast Socket:NCCL的高性能网络栈 提到了对NCCL 本身的优化,比较底层。
数据并行ps/allreduce
- 中心化分布式,存在一个中心节点,它的作用是汇总并分发其他计算节点的计算结果,更进一步,中心节点可以采用同步更新策略(Synchronous updating),也可以采用异步更新策略(Asynchronous updating)。一般情况下,参数服务器数目远少于工作机器,导致参数服务器端极易成为网络瓶颈。
- 去中心化分布式
embedding 场景下架构模式选择: 参数服务器适合的是高纬稀疏模型训练,它利用的是维度稀疏的特点,每次 pull or push 只更新有效的值。但是深度学习模型是典型的dense场景,embedding做的就是把稀疏变成稠密。所以这种 pull or push 的不太适合。而网络通信上更优化的 all-reduce 适合中等规模的深度学习。又比如由于推荐搜索领域模型的 Embedding 层规模庞大以及训练数据样本长度不固定等原因,导致容易出现显存不足和卡间同步时间耗费等问题,所以 all-reduce 架构很少被用于搜索推荐领域。