CUDA环境详解
本文主要介绍 CUDA 环境,这一堆东西网上有很多博客介绍过了,我再来一篇:),参考前辈们的文章,看能不能写的更清楚一点。读后仍有问题,欢迎留言交流。
CUDA APIs
CUDA是由NVIDIA推出的通用并行计算架构,通过一些CUDA库提供了一系列API供应用程序调用。开发者可调用这些API充分利用GPU来处理图像,视频解码等。
CUDA 的 API 其实是可以分层次来理解的,这也造成了有时会出现 nvcc -V
显示的 CUDA 版本和 nvidia-smi
显示的 CUDA 版本不一致的情况,其实这就是由于它们根本就是不同层次的 API,因此并不一定是一起安装的,也就不一定是同一个版本的。
CUDA API体系包括:CUDA函数库(CUDA Libraries),CUDA运行时API(CUDA Runtime API),CUDA驱动API(CUDA Driver API),结构图如下:
CUDA Driver API
GPU设备的抽象层,通过提供一系列接口来操作GPU设备,性能最好,但编程难度高,一般不会使用该方式开发应用程序。
用于支持driver API的必要文件(如libcuda.so
)是由GPU driver installer安装的。 我么你常用的 nvidia-smi
就属于这一类API。
CUDA Runtime API
对CUDA Driver API进行了一定的封装,调用该类API可简化编程过程,降低开发难度。
用于支持runtime API的必要文件(如libcudart.so
以及nvcc
)是由CUDA Toolkit installer安装的。
CUDA Toolkit Installer:有时可能会集成了GPU driver Installer。nvcc
是与CUDA Toolkit一起安装的CUDA compiler-driver tool,即 CUDA 的编译器,它只知道它自身构建时的 CUDA runtime版本。它不知道安装了什么版本的GPU driver,甚至不知道是否安装了GPU driver。
CUDA Libraries
是对CUDA Runtime API更高一层的封装,通常是一些成熟的高效函数库,开发者也可以自己封装一些函数库便于使用。
比如常见的 cuFFT、cuBLAS 库。
关系
应用程序可调用CUDA Libraries或者CUDA Runtime API来实现功能,当调用CUDA Libraries时,CUDA Libraries会调用相应的CUDA Runtime API,CUDA Runtime API再调用CUDA Driver API,CUDA Driver API再操作GPU设备。
cudatoolkit
几个概念辨析
- CUDA:为“GPU通用计算”构建的运算平台。
- cuDNN:为深度学习计算设计的软件库。
- CUDA Toolkit (Nvidia): CUDA完整的工具安装包,其中提供了 Nvidia 驱动程序、开发 CUDA 程序相关的开发工具包等可供安装的选项。包括 CUDA 程序的编译器、IDE、调试器等,CUDA 程序所对应的各式库文件以及它们的头文件。
- CUDA Toolkit (Pytorch): CUDA不完整的工具安装包,其主要包含在使用 CUDA 相关的功能时所依赖的动态链接库。不会安装驱动程序。
- NVCC 是CUDA的编译器,只是 CUDA Toolkit 中的一部分
注:CUDA Toolkit 完整和不完整的区别:在安装了CUDA Toolkit (Pytorch)后,只要系统上存在与当前的 cudatoolkit 所兼容的 Nvidia 驱动,则已经编译好的 CUDA 相关的程序就可以直接运行,不需要重新进行编译过程。如需要为 Pytorch 框架添加 CUDA 相关的拓展时(Custom C++ and CUDA Extensions),需要对编写的 CUDA 相关的程序进行编译等操作,则需安装完整的 Nvidia 官方提供的 CUDA Toolkit。
完整版cudatoolkit内容
一般的结构中,include 包含头文件,bin 包含可执行文件,lib 包含程序实现文件编译生成的library,src包含源代码,doc或help包含文档,samples包含例子。
- Compiler:NVCC
- Tools:分析器profiler、调试器debuggers等
- Libraries:科学库和实用程序库
- CUDA Samples:CUDA和library API的代码示例
- CUDA Driver:驱动,需要与“有CUDA功能的GPU”和“CUDA”都兼容。CUDA工具包都对应一个最低版本的CUDA Driver,CUDA Driver向后兼容。
conda下的cudatoolkit
通过 conda 安装 cudatoolkit 包含的库文件在 ~/anaconda3/lib
中,或者在 ~/anaconda3/pkgs/cudatoolkit-xxx/lib
中查看,grep 一下 libcu、libnpp、libnv 这几个名字查看即可,当然这样 grep 出来的也不一定确实 cudatoolkit 的文件,可能有 libcurl 之类的,只是初步过滤。
ls ~/anaconda3/lib/ | egrep 'libcu|libnpp|libnv'
ls ~/anaconda3/pkgs/cudatoolkit-11.0.221-h6bb024c_0/lib/ | egrep 'libcu|libnpp|libnv'
# 这里的11.0.221-h6bb024c_0z是笔者的版本。大家的可能不同,请自行查看确认
conda 的 cudatoolkit只包含pytorch或其他框架( tensorflow、xgboost、Cupy)会使用到的so库文件。
本机CUDA多版本管理
修改软连接
我们安装的不同版本的 CUDA Runtime API 一般都会在 /usr/local
目录下。
我们可以通过:
ls -l /usr/local | grep cuda
来查看自己的机器上有多少个cuda版本,通常不带版本号的 cuda
会是其他带版本号的cuda-x.x
的软链接。即像下面这样:
lrwxrwxrwx 1 root root 21 12月 10 2020 cuda -> /usr/local/cuda-11.1/
而我们只要把我们使用cuda时都指向这个软链接 /usr/local/cuda
,并在需要切换版本时切换这个软链接的指向即可。
sudo rm -rf cuda
sudo ln -s /usr/local/cuda-9.0 /usr/local/cuda
修改环境变量
注意此时如果nvcc -V
的输出还是更改之前的CUDA版本的话, 可能是环境变量中直接指定了 CUDA 版本路径,比如在 ~/.bashrc
中有类似以下的语句:
export PATH=/usr/local/cuda-11.1/bin:$PATH
export LD_LIBRARY_PATH=/usr/local/cuda-11.1/lib64:$LD_LIBRARY_PATH
此时我们要修改环境变量为(在命令行中执行仅当前终端生效,修改 ~/.bashrc 永久生效):
export PATH=/usr/local/cuda/bin:$PATH
export LD_LIBRARY_PATH=/usr/local/cuda/lib64:$LD_LIBRARY_PATH
就可以愉快地用指向 /usr/local/cuda
的软链接来控制本机的 CUDA 版本了。
附:几个环境变量的含义
PATH
PATH是可执行文件路径,是三个中我们最常接触到的,因为我们命令行中的每句能运行的命令,如ls、top、ps等,都是系统通过PATH找到了这个命令执行文件的所在位置,再run这个命令(可执行文件)。 比如说,在用户的目录~/mycode/
下有一个bin文件夹,里面放了有可执行的二进制文件、shell脚本等。如果想要在任意目录下都能运行上述bin文件夹的可执行文件,那么只需要把这个bin的路径添加到PATH即可,方法如下:
# vim ~/.bashrc
PATH=~/mycode/bin:$PATH
LIBRARY_PATH和LD_LIBRARY_PATH
这两个路径可以放在一起讨论,
LIBRARY_PATH
是程序编译期间查找动态链接库时指定查找共享库的路径LD_LIBRARY_PATH
是程序加载运行期间查找动态链接库时指定除了系统默认路径之外的其他路径
两者的共同点是库,库是这两个路径和PATH路径的区别,PATH是可执行文件。
两者的差异点是使用时间不一样。一个是编译期,对应的是开发阶段,如gcc编译;一个是加载运行期,对应的是程序已交付的使用阶段。
配置方法也是类似:
export LD_LIBRARY_PATH=XXXX:$LD_LIBRARY_PATH
Pytorch CUDA版本相关的几个常用的变量
我们通常考察 Pytorch 中的以下几个变量来确定当前 Pytorch 的 CUDA 信息:
torch.cuda.is_available()
,CUDA 是否可用。torch.version.cuda
,CUDA 版本。torch.utils.cpp_extension.CUDA_HOME
, CUDA 路径 CUDA_HOME。
可以通过下面这一句来在命令行中查看上述三个变量信息:
python -c "import torch; from torch.utils.cpp_extension import CUDA_HOME; print(torch.cuda.is_available(), torch.version.cuda, CUDA_HOME)"
Pytorch用的到底是哪个版本的CUDA?
其实就是看 CUDA_HOME 环境变量到底被指定为什么。
Pytorch 搜索 CUDA_HOME 的顺序如下:
-
显式指定的环境变量 CUDA_HOME
即如:
export CUDA_HOME=/usr/local/cuda11.5
即直接指定
/usr/local/cuda11.5
路径下的 CUDA 为使用的 CUDA。 -
默认搜索的路径
/usr/local/cuda
我们之前也提到过本机 CUDA 多版本管理通常是通过改变上述路径的软链接来实现,这也是 Pytorch 一个默认会搜索的路径。
-
nvcc 的所在目录
即
which nvcc
的上级目录,之前提到过,nvcc 是 CUDA 的编译器,但是,他对于 Pytorch 并不是必须的,即可能 nvcc 并不存在,但是 Pytorch 还是可以正常通过 cudatoolkit 来调用 GPU,即下一条。 -
cudatoolkit
如果上述都不存在,则
torch.utils.cpp_extension.CUDA_HOME
会被设置为None
。然后 Pytorch 会使用 conda 安装的 cudatoolkit,其路径为cudart 库文件目录的上级目录(此时可能是通过 conda 安装的 cudatoolkit,一般直接用 conda install cudatoolkit,就是在这里搜索到 cuda 库的)。
Ref:
[1]:NVIDIA Docker CUDA容器化原理分析
[2]:怎么在docker中使用nvidia显卡
[3]:[docker gpu报错Error response from daemon: could not select device driver ““ with capabilities: [[gpu]]](https://blog.csdn.net/weixin_44966641/article/details/123760614)