我们经常使用显卡来加速特定计算。在现代的分布式容器部署环境下,几个概念需要略作区分和辨析,以方便避免沟通的障碍。
概念辨析 #
GPU #
GPU ( Graphics Processing Unit ) 显卡用来表述一种硬件。AMD 等也有一些显卡,但是在机器学习领域,我们目前通常的 tensorflow, pytorch 等使用基于 CUDA 的加速,因此此处特指 Nvidia 的支持 CUDA 的各种并行加速器。和很多简单的硬件外设不一样的是,显卡相当于一个外部的、专用功能的独立处理设备,通常我们需要从内存中提交数据到显存,然后调用显卡的并发计算脚本(从 .cu 编译),然后再通讯从显卡中拷贝计算结果回本地,然后进行下一步计算。因此在云服务虚拟化的时候,CPU 算力可以很容易拆分,而显卡算力比较困难。
通常的 GPU 的工作流如下[1]:
Nvidia Driver 显卡驱动 #
Nvidia Driver 是在宿主机上安装的驱动程序。安装完毕后,由 Unix 一切设备皆文件原则,你可以在 /dev
目录下找到一些文件如下。nvidiactl
, nvidia-uvm
等文件是共用的,而 /dev/nvidia0
是第一张卡,如果有多个,你会看到 /dev/nvidia1
等。市面上所谓的基于容器的虚拟化通常是把对应的设备挂载到容器内部。特别的,qgpu 等有一些更加深入的修改,他们在虚拟容器中构建了虚拟的设备文件并转发。无论如何实现,通常的,基于容器的云架构需要你修改一些内核,然后在宿主中安装,通过硬件接口注册设备文件。
如上图所示,我们需要找到这个设备文件,才能和它通讯。
驱动下载的官方索引地址是:https://www.nvidia.com/en-us/drivers/unix/
CUDA #
CUDA ( 或者 Compute Unified Device Architecture) 是 Nvidia 提供的一个并发计算平台框架。其中编译器可以编译 .cu 文件为 device code, 调用的是 runtime API, 编译 C/C++/.. 等文件,调用 driver API 和设备交互。由于这些编译后都需要链接相关动态库(通常在 /usr/libX 下有些 libnvidia*.so
和 /usr/local/cuda/*/libcu*.so
库文件)因此这些必须的文件我们直接打包到了容器。
因此,程序实际调用的是容器内安装的 .so 库,和硬件交互使用的设备文件是宿主机驱动安装后,宿主识别硬件生成的。 应用层代码调用容器内的 CUDA API 和设备文件通讯。设备文件协议和宿主机的驱动相关。
Compute capability #
Compute capability 是 CUDA 和驱动交互时候的一个概念,不同的 GPU 架构实现有区别,因此为了兼容似乎是编译期间编译多份代码来实现。这个如果不匹配,通常要重新编译具体定位,报错通常是类似 toolchain xxx 之类的。
兼容性几点观点 #
对容器内的应用而言,我们对宿主环境的 CUDA 不感兴趣,使用的时候也不会调用宿主机的 libcu* 相关库文件。它只是作为某种指示提示“这个驱动推荐兼容哪个版本”,实际以容器内测试为准。
家用显卡 (1080Ti, 2080S, 3090 等)兼容性非常弱,CUDA 大版本不一样一定无法启动。小版本的话要求驱动版本略 >= CUDA 版本对应版本,但是不能大太多。关联表参考官网 Compute Capability 表 和 Release Note。
- 其中 Release Note 中 Table 3. CUDA Toolkit and Corresponding Driver Versions 列出了 CUDA 对应的驱动最低版本。家用显卡需要严格按照这个表格为对应的 CUDA 配置宿主机驱动。
V100, A100 等 data center 版本的卡可以宽松一些,但是具体需要实际尝试才能明确。此外我们还是一定要检查 compute capability,如果没有编译对应版本的机器码,那么会遇到类似
RuntimeError: CUDA error: no kernel image is available for execution on the device
的错误字样。这时候需要重编译对应的 CUDA 代码,通常的,如果是 torch 之类的,我们需要重新设置好环境变量TORCH_CUDA_ARCH_LIST
为类似7.0;7.5;8.6
这样然后重新编译二进制。对已编译的 torch, 可以尝试执行 python 脚本print(torch.cuda.get_arch_list())
列举确认。
基本功能测试 #
容器内执行类似如下代码:
echo "Torch Version: $(python -c 'import torch; print(torch.__version__)'), Is CUDA Available: $(python -c 'import torch; print(torch.cuda.is_available())')"
期待输出如下:
Torch Version: 1.8.1+cu101, Is CUDA Available: True
通常 +cu101
表示是适配 cuda 10.1 版本的,is available
期望为 True,如果为 False 表示至少这儿 CUDA 是没有工作的。
其它相关扩展阅读: #
- CUDA Toolkit: https://developer.nvidia.com/cuda-toolkit