使用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

    Nvtop代表NVidia TOP,是一个类似于htop的NVIDIA GPU任务监控器。它可以处理多个GPU并以htop熟悉的方式打印它们的详细信息。

    它显示进程,并以可视化方式显示内存和GPU统计数据。

    此应用程序需要从源代码构建(需要gccmake等),但说明易于遵循,构建速度很快。

  • gpustat

    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 时,你可能会发现 pytorchnvidia-smi 对它们的排序方式不同,因此 nvidia-smi 报告为 gpu0 的设备,可能会被 pytorch 分配为 gpu1pytorch 使用 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)