1. 💢问题描述
运行程序的时候提醒显存不够,查看了一下nvidia-smi ,确实显存占满了,但是GPU-Util,gpu利用率有三个都是0,只有一个是56% 搜索后发现这个现象的原因还比较普遍,但是似乎没有几个可以很好解决这个问题, 参考:
2. 🥗原因分析
2.1 GPU内存占用率(memory usage)
- GPU内存的占用率往往是由模型大小和batchsize决定的,如果发现GPU占用率很小,比如40%,70%等等。如果此时网络结构已经固定,则只需要改变batch size的大小,就可以尽量利用完整个GPU的内存。
- 对GPU内存占用率影响最大的还是模型大小,包括网络的宽度,深度,参数量,中间每一层的缓存,都会在内存中开辟空间来进行保存,模型本身会占用很大一部分内存。
- 其次是batch size的大小,也会占用影响内存占用率。batch size设置为128,与设置为256相比,内存占用率是接近于2倍关系。当你batch size设置为128,占用率为40%的话,设置为256时,此时模型的占用率约等于80%,偏差不大。所以在模型结构固定的情况下,尽量将batch size设置大,充分利用GPU的内存。
- GPU会很快的算完你给进去的数据,主要瓶颈在CPU的数据吞吐量上面。
2.2 GPU内存利用率(volatile GPU-Util)
volatile memory 易失性存储器
- 当没有设置好CPU的线程数时,这个参数是在反复的跳动的,0%,20%,70%,95%,0%。这样停息1-2 秒然后又重复起来。
- 其实是GPU在等待数据从CPU传输过来,当数据从总线传输到GPU之后,GPU逐渐计算起来,利用率会突然升高,但是GPU的算力很强大,0.5秒就基本能处理完数据,所以利用率接下来又会降下去,等待下一个batch的传入。
- 所以GPU利用率瓶颈在内存带宽和内存介质上以及CPU的性能上面。
- 提高GPU利用率的方式:
- 硬件上(最好):更换好的四代或者更强大的内存条,配合更好的CPU。
- 软件上(以pytorch为例):
- PyTorch框架的数据加载
Dataloader 类中包含了一些优化,包括num_workers (线程数)以及pin_memory ,可以提升速度。解决数据传输的带宽瓶颈和GPU的运算效率低的问题。 - 在TensorFlow下也类似的设置。
详细来说
- 首先要将num_workers(线程数)设置合理,4,8,16是几个常选的几个参数。
测试发现将num_workers设置的非常大,例如,24,32,等,其效率也不一定会变高,因为模型需要将数据平均分配到几个子线程去进行预处理,分发等数据操作,设高了反而影响效率。 当然,线程数设置为1,是单个CPU来进行数据的预处理和传输给GPU,效率也会低。 - 其次,当服务器或者电脑的内存较大,性能较好的时候,可以打开pin_memory,就省掉了将数据从CPU传入到缓存RAM里,再给传输到GPU上;为True,会直接映射到GPU的相关内存块上,省掉了一点数据传输时间。
2.3 torch.utils.data.dataloader
官方文档:torch.utils.data.dataloader 确实有两个重要的参数:
num_workers (int, optional): how many subprocesses to use for data
loading. ``0`` means that the data will be loaded in the main process.
(default: ``0``)
collate_fn (callable, optional): merges a list of samples to form a
mini-batch of Tensor(s). Used when using batched loading from a
map-style dataset.
pin_memory (bool, optional): If ``True``, the data loader will copy Tensors
into CUDA pinned memory before returning them. If your data elements
are a custom type, or your :attr:`collate_fn` returns a batch that is a custom type,
see the example below.
使用这个参数,可以将tensor拷贝到cuda的pinned memory,
关于pinned memory,可以参考文章:博客园6.1 CUDA: pinned memory固定存储,是一种加速数据从cpu到gpu传输速度的方式。
2.4 其他相关内容
https://www.zhihu.com/question/298616211
3. 🍔好的实践经验
在调试过程,
- 命令:
top 实时查看CPU的进程利用率,这个参数对应你的num_workers的设置; - 命令:
watch -n 0.5 nvidia-smi 每0.5秒刷新并显示显卡设置。实时查看你的GPU的使用情况,这是GPU的设置相关。 - 这两个配合好。包括batch_size的设置。
使用了watch -n 0.5 nvidia-smi 这个命令之后,才确实可以看到,其实GPU是在运行的,只是变化很快。一直在变动。
|