使用GPU
GPU监控
以下是如何从终端以多种方式轮询GPU状态的方法:
查看使用GPU的进程以及GPU的当前状态:
watch -n 1 nvidia-smi
实时查看使用统计数据的变化:
nvidia-smi --query-gpu=timestamp,pstate,temperature.gpu,utilization.gpu,utilization.memory,memory.total,memory.free,memory.used --format=csv -l 1
这种方式很有用,因为你可以看到变化轨迹,而不仅仅是执行
nvidia-smi
时显示的当前状态。要查看其他可查询的选项,请运行:
nvidia-smi --help-query-gpu
。-l 1
每1秒更新一次(--loop
)。你可以增加这个数字以减少更新频率。-f filename
将日志记录到文件中,但你将无法看到输出。因此,最好使用nvidia-smi ... | tee filename
,这样既可以显示输出,也可以记录结果。如果你想在运行3600秒后停止记录,请运行:
timeout -t 3600 nvidia-smi ...
更多详情,请参阅 有用的nvidia-smi查询。
最有可能的是,你只想跟踪内存使用情况,因此这可能已经足够:
nvidia-smi --query-gpu=timestamp,memory.used,memory.total --format=csv -l 1
与上述类似,但以百分比显示统计数据:
nvidia-smi dmon -s u
这显示了基本信息(使用率和内存)。如果你想查看所有统计数据,可以不带参数运行:
nvidia-smi dmon
要了解其他选项,请使用:nvidia-smi dmon -h
-
Nvtop代表NVidia TOP,是一个类似于htop的NVIDIA GPU任务监控器。它可以处理多个GPU并以htop熟悉的方式打印它们的详细信息。
它显示进程,并以可视化方式显示内存和GPU统计数据。
此应用程序需要从源代码构建(需要
gcc
、make
等),但说明易于遵循,构建速度很快。 -
nvidia-smi
类似的监控器,但更紧凑。它依赖于 pynvml 与nvml层通信。安装:
pip3 install gpustat
。以下是使用示例:
gpustat -cp -i --no-color
更多详情请见: https://github.com/nicolargo/nvidia-ml-py3
https://github.com/FrancescAlted/ipython_memwatcher
由于 GPU 内存是一种稀缺资源,因此最好在使用完 CUDA 上的任何内容后立即尝试释放它,然后再将新对象移动到 CUDA。通常,一个简单的 del obj
就能解决问题。然而,如果你的对象包含循环引用,尽管调用了 del()
,它也不会被释放,直到 python 调用 gc.collect()
。而在此之前,它仍会占用分配的 GPU 内存!这也意味着在某些情况下,你可能需要自己调用 gc.collect()
。
如果你想了解 python 垃圾收集器何时以及如何自动调用,请参阅 gc 和 这篇 文章。
峰值内存使用
如果你要对 Learner
fit()
这样的函数运行 GPU 内存分析器,你会注意到在第一个 epoch 时,它会引发非常大的 GPU RAM 使用峰值,然后稳定在一个低得多的内存使用模式。这是因为 pytorch 内存分配器试图以最有效的方式为加载的模型构建计算图和梯度。幸运的是,你不需要担心这个峰值,因为分配器足够智能,能够识别内存紧张的情况,并且它能够在使用较少内存的情况下完成同样的工作,只是效率稍低。通常,继续以 fit()
为例,分配器需要至少与正常运行所需的第二及后续 epoch 一样多的内存。你可以在这里阅读关于这个话题的精彩讨论 这里。
pytorch 张量内存跟踪
显示所有当前分配的张量:
import torch
import gc
for obj in gc.get_objects():
try:
if torch.is_tensor(obj) or (hasattr(obj, 'data') and torch.is_tensor(obj.data)):
print(type(obj), obj.size())
except: pass
注意,gc 不会包含一些在 autograd 内部 消耗内存的张量。
这里有一个关于这个话题的 讨论,包含更多相关的代码片段。
GPU 重置
如果由于某种原因在退出 python 进程后 GPU 没有释放内存,你可以尝试重置它(将 0 更改为所需的 GPU ID):
sudo nvidia-smi --gpu-reset -i 0
在使用多进程时,有时一些客户端进程会卡住并变成僵尸进程,不会释放 GPU 内存。它们也可能对 nvidia-smi
不可见,因此它会报告没有使用的内存,但该卡无法使用,并且在尝试在该卡上创建一个微小的张量时会因 OOM 而失败。在这种情况下,使用 fuser -v /dev/nvidia*
定位相关进程并用 kill -9
终止它们。
这篇博客 文章 建议使用以下技巧来安排进程按需干净退出:
if os.path.isfile('kill.me'):
num_gpus = torch.cuda.device_count()
for gpu_id in range(num_gpus):
torch.cuda.set_device(gpu_id)
torch.cuda.empty_cache()
exit(0)
在你将这段代码添加到训练迭代后,一旦你想停止它,只需进入训练程序的目录并运行
touch kill.me
多 GPU
GPU 顺序
当你拥有多个 GPU 时,你可能会发现 pytorch
和 nvidia-smi
对它们的排序方式不同,因此 nvidia-smi
报告为 gpu0
的设备,可能会被 pytorch
分配为 gpu1
。pytorch
使用 CUDA GPU 排序,这是通过 计算能力(计算能力更高的 GPU 优先)完成的。
如果你想让 pytorch
使用 PCI 总线设备顺序,以匹配 nvidia-smi
,请设置:
export CUDA_DEVICE_ORDER=PCI_BUS_ID
在启动程序之前(或将其放入你的 ~/.bashrc
中)。
如果你只想在特定的 GPU ID 上运行,可以使用 CUDA_VISIBLE_DEVICES
环境变量。它可以设置为单个 GPU ID 或一个列表:
export CUDA_VISIBLE_DEVICES=1
export CUDA_VISIBLE_DEVICES=2,3
如果你不在 shell 中设置环境变量,可以在代码的开头使用以下方法设置:import os; os.environ['CUDA_VISIBLE_DEVICES']='2'
。
一个不太灵活的方法是在代码中硬编码设备 ID,例如将其设置为 gpu1
:
torch.cuda.set_device(1)