跳转至

性能篇

如果发现训练速度明显很慢时,先排除是实例本身的问题,点击下载尝试调整网络结构或batch size把GPU压满,如测试脚本能正常压满GPU,请按照以下方式进行排查和调优。

检查NumPy

该项中招率非常高,所以优先检查。

NumPy会使用OpenBlas或MKL做计算加速。Intel的CPU支持MKL,AMD CPU仅支持OpenBlas。如果使用Intel的CPU,MKL会比OpenBlas有几倍的性能提升(部分矩阵计算),对最终的性能影响非常大。一般来说AMD CPU使用OpenBlas会比Intel的CPU使用OpenBlas更快,因此不用过份担心AMD CPU使用OpenBlas的性能差。

如果您在使用Intel CPU,先验证自己使用的NumPy是MKL还是OpenBlas版本。

image-20220507151551096

有以上mkl字样代表是MKL的版本。

在使用清华等国内的Conda源时,安装NumPy时默认会使用OpenBlas的加速方案,您使用conda install numpy安装时会发现如下OpenBlas相关的包:

image-20220415135533064

所以为了安装MKL的NumPy,解决方法如下:

# 第一步:卸载当前的NumPy
pip uninstall numpy (如果是conda安装的, conda uninstal numpy)
# 第二步:删除国内的Conda源
echo "" > /root/.condarc
# 第三步:重新安装NumPy
conda install numpy

如果以上步骤正确,那么在install numpy时将会看到:

image-20220415135910439

重装NumPy以后再次执行您的程序,查看是否性能有提升。如果不是NumPy的原因,请继续看下文:

瓶颈分析

首先确认您正在训练的模型具备什么样的特点,在性能上可以分为以下几种情况:

  1. 小模型、数据预处理简单。比如用LeNet训练MNIST,这种情况优化的余地小,因为模型本身对算力的需求小,适合用一般的GPU来训练即可,用越好的GPU使用率会越低。这种场景GPU的使用率特点是保持在一个较低的水平,但是波动小。
  2. 小模型、数据预处理较复杂。比如用ResNet18层网络跑ImageNet分类,这种情况CPU预处理会占用更长周期,而GPU的计算非常快占用时长短,因此适合选择更好的CPU和一般的GPU。这种场景GPU的使用率特点是波动大,峰值比较高,然后大部分时间都很低。
  3. 大模型、数据预处理简单。这种情况一般GPU都会利用很高且波动小,但是对磁盘的要求也很高,如果利用率低那么请参考下述的方法压榨性能。
  4. 大模型,数据预处理复杂。这种情况对CPU和GPU的要求都很高,都可能成为瓶颈,包括磁盘性能,需具体算法具体分析。

对于以上1和2两种情况,优化余地较小,更适合从选择主机下手配合代码优化,提高经济性。对于3和4如果发现GPU利用率较低时可以按下述方法排查瓶颈,优化性能。

如果GPU始终没有利用率,请确认:3060、3090、3080Ti、A4000、A40、A100、A5000等安培架构的卡需要cuda11.x才能使用(最好cuda11.1及以上),请使用较高版本的框架。

Step.1 查看GPU的利用率

在终端中执行nvidia-smi -l 1命令

user@seeta:/tmp/test_directory$ nvidia-smi -l 1
Mon Nov  8 11:55:26 2021       
+-----------------------------------------------------------------------------+
| NVIDIA-SMI 440.82       Driver Version: 440.82       CUDA Version: 10.2     |
|-------------------------------+----------------------+----------------------+
| GPU  Name        Persistence-M| Bus-Id        Disp.A | Volatile Uncorr. ECC |
| Fan  Temp  Perf  Pwr:Usage/Cap|         Memory-Usage | GPU-Util  Compute M. |
|===============================+======================+======================|
|   0  TITAN X (Pascal)    Off  | 00000000:01:00.0  On |                  N/A |
| 31%   57C    P0    66W / 250W |    408MiB / 12194MiB |      2%      Default |
+-------------------------------+----------------------+----------------------+
|   1  TITAN X (Pascal)    Off  | 00000000:04:00.0 Off |                  N/A |
| 93%   27C    P8    11W / 250W |      2MiB / 12196MiB |      0%      Default |
+-------------------------------+----------------------+----------------------+

+-----------------------------------------------------------------------------+
| Processes:                                                       GPU Memory |
|  GPU       PID   Type   Process name                             Usage      |
|=============================================================================|
|    0      1450      G   /usr/lib/xorg/Xorg                            32MiB |
|    0      2804      G   /usr/lib/xorg/Xorg                           351MiB |
+-----------------------------------------------------------------------------+

如果GPU占用率为0说明代码可能没有使用GPU,需检查代码。

如果GPU占用率忽高忽低或者占用率只能到50%左右,那么可能是CPU或磁盘IO出现瓶颈,请看下述步骤。

Step.2 查看CPU的占用率和内存使用情况

请在控制台 -> 我的实例中找到实例监控按钮

image-20211220193538333

进去查看CPU和内存的使用:

  1. 如果内存使用较高说明可能是内存不够,此时您可以通过升配GPU(内存大小按GPU数量成比例分配)来排除该问题。
  2. 假设您的实例核心数为5,如果CPU占用率接近500%(即5个核心都正在高负载使用)那么可能是CPU数量不够,CPU出现了瓶颈,此时您可以迁移实例到更高CPU数量的主机上去或者升配。如果CPU占用率远没有达到500%的,说明您的代码没有把CPU的算力压榨出来,一般可以通过修改Dataloader中的worker_num提高CPU负载,经验值num_worker=核心数量,最好调参测试性能。(该项一般能解决80%的场景)

Step.3 查看磁盘性能

使用命令:dd if=/dev/zero of=/root/autodl-tmp/zero count=100 bs=10M

$ dd if=/dev/zero of=/root/autodl-tmp/zero.bin count=1000 bs=1M
1000+0 records in
1000+0 records out
1048576000 bytes (1.0 GB, 1000 MiB) copied, 0.584865 s, 1.8 GB/s

清理测试文件
$ rm /root/autodl-tmp/zero.bin  

从输出日志中可以看出1.8GB/s的IO性能,如果IO性能低于50MB/s,说明磁盘性能较差(一般机械硬盘的IO速度为150MB/s左右),可以判断为磁盘性能较差,此时可以更换其他主机。

Step.4 检查代码

如果上述步骤都没有解决问题,那么请查看和分析您的代码,找到瓶颈所在。如果仍然无法解决,重启实例 - > 更换主机。代码层面有几种常见的影响性能的写法,请自查:

  1. 每次迭代中做一些与计算无关的操作。比如保存测试图片等等,解决办法是拉长保存测试图片的周期,不要每次迭代都做额外耗时的操作
  2. 频繁保存模型,导致保存模型占用了训练过程一定比例的时间
  3. PyTorch的官方性能优化指南:查看
  4. TensorFlow的官方性能优化指南:查看
  5. 同时欢迎在网站提交反馈其他case,共享知识

Torch.Profiler

更多用户参考[官方文,简单使用可以:

# 添加profile上下文
with torch.profiler.profile(
    activities=[
        torch.profiler.ProfilerActivity.CPU,
        torch.profiler.ProfilerActivity.CUDA,
    ]
) as p:
    # code to profile
    for epoch in range(epochs):
        for batch in dataloader:
            # ...

# 打印耗时    
print(p.key_averages().table(
    sort_by="self_cuda_time_total", row_limit=-1))

从打印出来的表格中可以看到CPU和GPU的分别耗时,以及算子的耗时,从而更精准的分析性能瓶颈。

其他经验

  1. 如果您在使用单机多卡并行,并且使用了PyTorch框架,那么一般将torch.nn.DataParallel (DP)更换为torch.nn.DistributedDataParallel (DDP)能提升性能。官方原文是:DistributedDataParallel offers much better performance and scaling to multiple-GPUs.
  2. 如果使用RTX 3090等安培架构的NVIDIA GPU,使用最新版本的PyTorch 1.9和1.10相比1.7会有较大的性能提升,PyTorch 1.7和1.8性能较差。(关机后在更多操作中更换PyTorch1.10的镜像)
  3. 平台使用上,如果你的算法是非常吃资源的类型,那么最好在多个实验同时调参时,选择在不同主机上开多个实例,每个实例中跑一个实验,而避免在同主机上开实例或者同实例中租多卡GPU,不同GPU分别跑实验。