多卡训练模型时绕不过的一个问题:DataParallel(DP)和DistributedDataParallel(DDP)有什么区别?
单机单卡不用考虑。
多机多卡用DDP也不用多想。
单机多卡用DP和DDP有啥区别?为什么DDP比DP要快?


PS:关于平行与分布式训练,pytorch官网的tutorial里有详细的一章节描述,建议去看看。本文用中文做一个简短的总结与解释。

起源

首先,这个问题起源于pytorch的 torch.nn.DataParallel 文档中的一段警告:

This is the highly recommended way to use DistributedDataParallel , with multiple processes, each of which operates on a single GPU. This is currently the fastest approach to do data parallel training using PyTorch and applies to both single-node(multi-GPU) and multi-node data parallel training. It is proven to be significantly faster than DataParallel for single-node multi-GPU data parallel training.

DP应该是历史遗留,并非一开始设计出来就是被打脸的。

解释

DDP 是多进程并行模式,这就使得DDP可以在不同的计算机上运行。

而DP是单进程多线程并行模式。因此它只能在单个设备上运行。

现在问题变得很简单,DDP和DP的区别可以理解为多进程多线程之间的区别,这是本科生面试常被问到的操作系统相关的问题。网络上有很多解释。简单来说线程是进程的一部分,一个进程可以有多个线程,一个进程内的多个线程要通过抢执行权获得执行时间。

这里提一下我之前的一篇博客GIL in Python,我解释过线程下面线程的执行方式:

  1. 获取GIL;
  2. 执行代码直到sleep或者Python虚拟机将其挂起;
  3. 释放GIL

一个python进程只有一个GIL,线程要抢到GIL才能被CPU执行。

回到DP和DDP的问题上。由于DP是单进程的,跨多个线程的GIL竞争以及每次迭代分散数据和收集数据以及模型复制会带来额外的开销。这就导致了即使是单机多卡,DP也比DDP慢。

具体地,在单机多卡环境下,DDP的优点:

  1. 每个进程都有自己的optimizer,每次迭代都进行完整的优化步骤。由于梯度已经在各个进程中收集起来并作了平均,所以这对每个进程都是相同的(总共操作一次),就不再需要参数传播,节约了节点间传递张量的时间。
  2. 每个进程都有自己的python解释器,从而消除了由单个python进程驱动多个执行线程,模型副本或GPU所带来的额外解释器开销以及GIL竞争问题。这对那些包含循环层或许多小组件,即使用python runtime很多的模型十分重要。

用法

DataParallel

DP的使用十分简单,一行代码解决。

net = torch.nn.DataParallel(model, device_ids=[0, 1, 2])	

注意,无论是使用了DP还是DDP,模型都会升格,原模型成为当前模型的一个module,即net = net.module。所以如果不修改模型保存时的代码,在load pretrain的时候,需要注意这一点,否则会出现参数key不匹配问题。

DistributedDataParallel

官方有一个详细的tutorial,详情