Some benchmark of distributed training
单节点单卡测试
Note: 可profile
单节点多卡数据并行,具体使用torch中的nn.DataParallel(model)实现
Note: 可profile
多节点多卡数据并行,使用from torch.nn.parallel import DistributedDataParallel实现
DDP和DP的区别在于,在使用DDP实例化模型之前,需要先设置好通信的机制,具体可以参考DDP.py中的 setup函数。
Note: 可profile
使用Slurm进行实验,先写好一个sbatch用的脚本,注意脚本文件里面用的是srun命令。 eg:
#!/bin/bash
#SBATCH -N 4
#SBATCH --ntasks-per-node=1
#SBATCH -p gpu
#SBATCH --gres=gpu:1
#SBATCH --no-requeue
source ~/dat01/lpeng/env.bash # 加载环境变量
#Used for ddp
srun -N 4 --ntasks-per-node=1 --gres=gpu:1 -p gpu python -u DDP.py
在对应的python文件中,需要通过os库来获取对应的slurm环境变量,来设置setup函数中所需要的对应的rank。
eg:
rank = int(os.environ['SLURM_PROCID']) # 全局的rank,用于init_process_group
local_rank = int(os.environ['SLURM_LOCALID']) # 一个节点上的rank,用于gpu的分配
world_size = int(os.environ['SLURM_NTASKS']) # 进程总数,用于init_process_group
Note: 可profile
使用两块卡进行模型并行的测试,需要手动划分模型,即将子模型手动.to()到对应的GPU上。
eg:
self.seq1 = nn.Sequential(
self.conv,
self.conv1,
self.bn1,
self.relu,
self.maxpool,
self.layer1,
self.layer2
).to('cuda:0')
self.seq2 = nn.Sequential(
self.layer3,
self.layer4,
self.avgpool,
).to('cuda:1')
x = self.seq2(self.seq1(x).to('cuda:1'))
Note: 可profile
在继承了MP模型的基础上,修改forward过程,将每次输入的batch分成更小的micro-batch,进行前向传播训练, 此处利用到了torch异步执行的机制。
注意:PMP的backward和step是没有做优化的。
def forward(self, x):
splits = iter(x.split(self.split_size, dim=0))
s_next = next(splits)
s_prev = self.seq1(s_next).to('cuda:1')
ret = []
for s_next in splits:
# A. s_prev runs on cuda:1
s_prev = self.seq2(s_prev)
ret.append(self.fc(s_prev.view(s_prev.size(0), -1)))
# B. s_next runs on cuda:0, which can run concurrently with A
s_prev = self.seq1(s_next).to('cuda:1')
s_prev = self.seq2(s_prev)
ret.append(self.fc(s_prev.view(s_prev.size(0), -1)))
Note: 可profile
使用RPC库实现一个PMP,可能由于测试机群的通信机制的问题,效果非常的差。也有可能是当前torch的支持不好。
zero的思想可以参考 ref: DeepSpeed
torch1.8的ZeRO优化器属于ZeRO-1,只对优化器状态进行划分。
目前torch1.8的ZeRO优化器实现还是比较慢的,现在还是beta版本就先忍了。吞吐率奇低,大概率是因为每一次step的时候都需要进行很多次的通信, 也可能是因为我所用的机群通信太慢的原因。
Note: 应该可profile,没试过
只支持单节点。
torchgpipe现在已经被加入了torch1.8豪华套餐,我还没有用1.8中的torchgpipe做过测试, 但是在torchgpipe的原版代码中我进行过一点测试。基本上micro-batch数量越多,加速比越大, 不过也存在一个加速的上限,曲线为凸型。 4卡,micro-batch为32个的情况下,加速比为2.8。
Note: 暂时不支持profile
deepspeed是zero的老家,降低内存界的扛把子。 ref: DeepSpeed
支持多机多卡,使用时只需要deepspeed.initialize一下就可以了,然后用model_engine进行模型的forward, backward和step即可。
model_engine, optimizer, trainloader, __ = deepspeed.initialize(
args=args, model=model, model_parameters=parameters, training_data=train_data)
...
outputs = model_engine(inputs)
loss = criterion(outputs, labels)
model_engine.backward(loss)
model_engine.step()
在slurm机群中使用时需要先生成一个hostfile,命令如下:
srun hostname -s > hostfile.txt
然后利用edit_hostfile改成DeepSpeed所需要的格式,最后用DeepSpeed启动任务。
具体可参考运行的脚本:run_ds.sh
Note: 暂时不支持profile
注意:这个实验里面暂时还没用到ZeRO,目前只是单纯的多节点数据并行,后面再进行修改。 实验的时候没有考虑到batch_size需要重新调整(DeepSpeed把batch_size平均分了), 所以在实验结果里面看着会有内存大大减低了的错觉。
测试环境: torch1.6 (ZeRO-DDP on torch1.8) + cuda10.2; deepspeed=0.3.10
模型: ResNet50
数据集: MNIST
batch_size : 64
实验名字 | GPU type&num | Throughput | Memory Usage | Accuracy |
---|---|---|---|---|
benchmark | V100 | 332/s | 7.7GB | 0.98 |
DP | 2 V100 | 583/s | 7.7GB each | 0.97 |
DP | 4 V100 | 818/s | 7.7GB | 0.86 |
Manual MP | 2 V100 | 331/s | 5.9GB / 3.4GB | 0.94 |
Pipelined MP | 2 V100 | 398/s | 5.6GB / 3.1GB | 0.98 |
RPC PMP (split_size=8) | 2 V100 | 146/s | 8.1GB / 7.5GB | - |
DDP | 2 V100 x 1 node | 516/s | 8.9GB / 7.6GB | 0.97 |
DDP | 1 V100 x 2 node | 513/s | 7.7GB / 7.7GB | 0.95 |
DDP | 1 V100 x 4 node | 880/s | 7.7GB each | 0.96 |
DDP | 4 V100 x 1 node | 131/s | 7.7GB each | 0.96 |
Zero-DDP | 2 V100 x 1 node | 43/s | 8.0GB each | 0.96 |
Zero-DDP | 4 V100 x 1 node | 38/s | 8.0GB each | 0.92 |
DeepSpeed-DP | 2 V100 x 1 node | 580/s | 4.6GB each | 没测 |
DeepSpeed-DP | 1 V100 x 2 node | 575/s | 4.6GB each | 没测 |
DeepSpeed-DP | 4 V100 x 1 node | 809/s | 3.2GB each | 没测 |
DeepSpeed-DP | 1 V100 x 4 node | 575/s | 3.14GB each | 没测 |
DeepSpeed-DP | 2 V100 x 2 node | 564/s | 3.14GB each | 没测 |
DeepSpeed-DP | 4 V100 x 2 node | 731/s | 2.44GB each | 没测 |
DeepSpeed-DP | 2 V100 x 4 node | 477/s | 2.44GB each | 没测 |
- DDP 在单节点4块卡上可能存在通信的问题,在两个环境上测过均有这一现象
- 可以看出DeepSpeed很依赖节点间的通信速率,因此节点数量多的时候,吞吐率反而下降了。可以尝试换个实验环境测一下