NVIDIAドライバを更新せずに新しいCUDAを使う(データセンタ向けGPU限定)
初めに
NVIDIA製のGPUを使い、CUDAを使ったアプリケーションを開発・利用する際に悩まされるのが「NVIDIAドライバのバージョン」と「CUDAのバージョン」の整合性です。
この整合性問題について、CUDA 10から「Forward Compatible Upgrade」という方法が使えるようになり、端的に言えば「古いNVIDIAドライバで新しいCUDAを使う」ことができるようになりました。
・・・ただし、データセンタ向けGPU(Teslaなど)限定です。GeForceなど、コンシューマ向けのGPUではこの方法は使用できません。
前提知識: NVIDIAドライバ、CUDAのバージョンについて
通常、あるバージョンのCUDA(ランタイム、及びツールキット)を使うためには、指定バージョン以上のNVIDIAドライバが必要です。
公式ページ「CUDA Compatibility :: GPU Deployment and Management Documentation」(以下、CUDA互換性ページ)の「Table 1. CUDA Toolkit and Minimum Required Compatible Driver Versions」を以下に引用します。
上表から、例えば「CUDA 11.3」を使うためには450.80.02
以上のNVIDIAドライバが必要なことが分かります。
Linux環境では、使用しているNVIDIAドライバのバージョン、CUDAドライバAPIのバージョンは、nvidia-smi
コマンドで確認することができます。
$ nvidia-smi | head -n 4
XXX XXX XX XX:XX:XX 2021
+-----------------------------------------------------------------------------+
| NVIDIA-SMI 418.67 Driver Version: 418.67 CUDA Version: 10.1 |
|-------------------------------+----------------------+----------------------+
上記の例では、418.67
がNVIDIAドライバのバージョン、10.1
がCUDAドライバAPIのバージョンです。
「CUDAドライバAPIのバージョン」は、「このNVIDIAドライバで利用可能なCUDAランタイム/ツールキットの最大バージョン」と思っておけば大丈夫です。(実際にインストールされているCUDAツールキットのバージョンではありません)
実際にPATHが通っているCUDAツールキットのバージョンを知りたい場合は、nvcc --version
コマンドを使うことができます。
$ nvcc --version
nvcc: NVIDIA (R) Cuda compiler driver
Copyright (c) 2005-2016 NVIDIA Corporation
Built on Tue_Jan_10_13:22:03_CST_2017
Cuda compilation tools, release 8.0, V8.0.61
「CUDAツールキットのバージョン」と「CUDAドライバAPIのバージョン」は混同しがちなので注意が必要です。
また、CUDAツールキットは1つの環境に複数インストールすることも多いため、どのバージョンにPATHが通っているかも注意が必要です。
通常のアップグレードパス
通常、新しいバージョンのCUDAツールキット/ランタイムを使用したい場合、NVIDIAドライバを更新(バージョンアップ)します。
CUDA互換性ページの「Figure 2. CUDA Upgrade Path」を以下に引用します。
NVIDIAドライバのパッケージには、カーネルモードドライバのnvidia.ko
と、ユーザモードライブラリのlibcuda.so
などが含まれており、通常のアップグレードパスでは、この両方を同時に更新します。
ただ、NVIDIAドライバの更新には管理者権限が必要であり、GPUを利用しているプロセスの停止が必要です。また、GPUクラスタなどの場合、複数のマシンについてNVIDIAドライバを更新する必要があるため、それなりの手間が必要です。
新しいアップグレードパス
一方、CUDA 10.0(をサポートするNVIDIAドライバ)からは「Forward Compatible Upgrade」という方法を使えるようになりました。
ただし、この方法をサポートするのは、データセンタ向けGPU、かつLinux版のみです。
CUDA互換性ページの「Figure 3. Forward Compatibility Upgrade Path」を以下に引用します。
上手の通り、カーネルモードドライバのnvidia.ko
を更新せずに、ユーザモードライブラリのlibcuda.so
などを更新できるようになりました。
この方法では、NVIDIAドライバのEOL(End of Life)までにリリースされたすべてのCUDAバージョンがサポートされます。
例えば、前述のNVIDIAドライバ418.67
(R418、LTSB版)の場合、EOLは2022年3月です。
NVIDIAドライバのブランチの種類、EOLについては「NVIDIA Datacenter Drivers :: NVIDIA Tesla Documentation」をご参照ください。
実際の手順
では実際に、古いNVIDIAドライバ環境で新しいCUDAを使ってみましょう。手順は以下の通りです。
- NVIDIAドライバをダウンロードする。
- NVIDIAドライバを展開(解凍)する。
-
so
ファイルのシンボリックリンクを作成する。 -
LD_LIBRARY_PATH
環境変数を設定する。 - アプリケーションを実行する。
他にもcuda-compat-*
パッケージを用いる方法があるようですが、未検証です。詳しくは、CUDA互換性ページをご参照ください。
1. NVIDIAドライバをダウンロードする
まずは最新版のNVIDIAドライバをダウンロードします。コンシューマ向け(GeForceなど)とデータセンタ向け(Teslaなど)ではバージョンが異なりますので注意が必要です。
今回は以下のページから460.73.01
(リリース日: 2021年4月19日)をダウンロードしました。
NVIDIA DRIVERS Data Center Driver for Linux x64
2. NVIDIAドライバを展開(解凍)する
ダウンロードしたNVIDIAドライバ(run
ファイル)を展開します。インストールは行いません。
通常、run
ファイルを用いてNVIDIAドライバをインストールする場合、管理者権限、かつオプションなしで実行します。
ですが、今回は展開を行いたいだけなので、一般ユーザ権限、かつ-x
オプション付きで実行します。
$ sha1sum NVIDIA-Linux-x86_64-460.73.01.run
86a46deaf64b4d2e13416145a205cc079fc93358 NVIDIA-Linux-x86_64-460.73.01.run
$ chmod 755 NVIDIA-Linux-x86_64-460.73.01.run
$ ./NVIDIA-Linux-x86_64-460.73.01.run -x
Creating directory NVIDIA-Linux-x86_64-460.73.01
Verifying archive integrity... OK
Uncompressing NVIDIA Accelerated Graphics Driver for Linux-x86_64 460.73.01...............................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................
NVIDIA-Linux-x86_64-460.73.01
ディレクトリが作成され、そのディレクトリ内にファイルが展開されます。
$ cd NVIDIA-Linux-x86_64-460.73.01
$ ls -l *.so*
-rwxr-xr-x 1 yuya_kato yuya_kato 80224 Apr 2 06:30 libEGL.so.1.1.0
-rwxr-xr-x 1 yuya_kato yuya_kato 26752 Apr 2 06:30 libEGL.so.460.73.01
-rwxr-xr-x 1 yuya_kato yuya_kato 1312784 Apr 2 06:33 libEGL_nvidia.so.460.73.01
-rwxr-xr-x 1 yuya_kato yuya_kato 649320 Apr 2 06:30 libGL.so.1.7.0
-rwxr-xr-x 1 yuya_kato yuya_kato 43104 Apr 2 06:30 libGLESv1_CM.so.1.2.0
-rwxr-xr-x 1 yuya_kato yuya_kato 67880 Apr 2 06:32 libGLESv1_CM_nvidia.so.460.73.01
-rwxr-xr-x 1 yuya_kato yuya_kato 79968 Apr 2 06:30 libGLESv2.so.2.1.0
-rwxr-xr-x 1 yuya_kato yuya_kato 117032 Apr 2 06:32 libGLESv2_nvidia.so.460.73.01
-rwxr-xr-x 1 yuya_kato yuya_kato 137512 Apr 2 06:30 libGLX.so.0
-rwxr-xr-x 1 yuya_kato yuya_kato 1211504 Apr 2 06:31 libGLX_nvidia.so.460.73.01
-rwxr-xr-x 1 yuya_kato yuya_kato 952472 Apr 2 06:30 libGLdispatch.so.0
-rwxr-xr-x 1 yuya_kato yuya_kato 30856 Apr 2 06:47 libOpenCL.so.1.0.0
-rwxr-xr-x 1 yuya_kato yuya_kato 198752 Apr 2 06:30 libOpenGL.so.0
-rwxr-xr-x 1 yuya_kato yuya_kato 21795104 Apr 2 06:48 libcuda.so.460.73.01
-rwxr-xr-x 1 yuya_kato yuya_kato 12680840 Apr 2 06:35 libglxserver_nvidia.so.460.73.01
-rwxr-xr-x 1 yuya_kato yuya_kato 5008168 Apr 2 06:33 libnvcuvid.so.460.73.01
-rwxr-xr-x 1 yuya_kato yuya_kato 90480 Apr 2 06:30 libnvidia-allocator.so.460.73.01
-rwxr-xr-x 1 yuya_kato yuya_kato 731648 Apr 2 06:32 libnvidia-cbl.so.460.73.01
-rwxr-xr-x 1 yuya_kato yuya_kato 221552 Apr 2 06:30 libnvidia-cfg.so.460.73.01
-rwxr-xr-x 1 yuya_kato yuya_kato 50412024 Apr 2 06:51 libnvidia-compiler.so.460.73.01
-rwxr-xr-x 1 yuya_kato yuya_kato 43680 Apr 2 06:33 libnvidia-egl-wayland.so.1.1.5
-rwxr-xr-x 1 yuya_kato yuya_kato 28473248 Apr 2 06:48 libnvidia-eglcore.so.460.73.01
-rwxr-xr-x 1 yuya_kato yuya_kato 112664 Apr 2 06:32 libnvidia-encode.so.460.73.01
-rwxr-xr-x 1 yuya_kato yuya_kato 129760 Apr 2 06:30 libnvidia-fbc.so.460.73.01
-rwxr-xr-x 1 yuya_kato yuya_kato 30332968 Apr 2 06:50 libnvidia-glcore.so.460.73.01
-rwxr-xr-x 1 yuya_kato yuya_kato 635400 Apr 2 06:32 libnvidia-glsi.so.460.73.01
-rwxr-xr-x 1 yuya_kato yuya_kato 12494904 Apr 2 06:47 libnvidia-glvkspirv.so.460.73.01
-rwxr-xr-x 1 yuya_kato yuya_kato 1367368 Apr 2 06:31 libnvidia-gtk2.so.460.73.01
-rwxr-xr-x 1 yuya_kato yuya_kato 1376040 Apr 2 06:31 libnvidia-gtk3.so.460.73.01
-rwxr-xr-x 1 yuya_kato yuya_kato 219736 Apr 2 06:32 libnvidia-ifr.so.460.73.01
-rwxr-xr-x 1 yuya_kato yuya_kato 1770712 Apr 2 06:32 libnvidia-ml.so.460.73.01
-rwxr-xr-x 1 yuya_kato yuya_kato 3301608 Apr 2 06:33 libnvidia-ngx.so.460.73.01
-rwxr-xr-x 1 yuya_kato yuya_kato 17384136 Apr 2 06:49 libnvidia-opencl.so.460.73.01
-rwxr-xr-x 1 yuya_kato yuya_kato 43000 Apr 2 06:32 libnvidia-opticalflow.so.460.73.01
-rwxr-xr-x 1 yuya_kato yuya_kato 10521080 Apr 2 06:37 libnvidia-ptxjitcompiler.so.460.73.01
-rwxr-xr-x 1 yuya_kato yuya_kato 68126944 Apr 2 06:54 libnvidia-rtcore.so.460.73.01
-rwxr-xr-x 1 yuya_kato yuya_kato 18456 Apr 2 06:30 libnvidia-tls.so.460.73.01
-rwxr-xr-x 1 yuya_kato yuya_kato 116719176 Apr 2 06:56 libnvoptix.so.460.73.01
-rwxr-xr-x 1 yuya_kato yuya_kato 712400 Apr 2 06:30 libvdpau_nvidia.so.460.73.01
-rwxr-xr-x 1 yuya_kato yuya_kato 6373440 Apr 2 06:37 nvidia_drv.so
上記の通り、各種ユーザモードライブラリ(*.so
)が含まれています。
so
ファイルのシンボリックリンクを作成する
3. 展開したユーザモードライブラリは、詳細なバージョン番号付きのファイル名(例: libcuda.so.460.73.01
)となっています。
このままではファイル名が違って共有ライブラリをロードできないため、ビルド済みのアプリケーションに合わせてシンボリックリンクを作成します。
$ ln -s libcuda.so.460.73.01 libcuda.so
$ ln -s libcuda.so.460.73.01 libcuda.so.1
多くの場合はlibcuda.so
、libcuda.so.1
というファイル名で参照できれば大丈夫と思います。
ここではlibcuda.so.460.73.01
に対するシンボリックリンクのみ作成していますが、必要に応じてldd
コマンドなどでリンクしている共有ライブラリを確認してください。
LD_LIBRARY_PATH
環境変数を設定する
4. ユーザモードライブラリを展開しただけでは、システムは共有ライブラリを探索してくれないため、共有ライブラリのロードパスであるLD_LIBRARY_PATH
環境変数を設定します。
具体例を以下に示します。
$ export LD_LIBRARY_PATH=/home/yuya_kato/NVIDIA-Linux-x86_64-460.73.01:${LD_LIBRARY_PATH}
ここではLD_LIBRARY_PATH
環境変数を用いていますが、ビルド時のパス設定であるRPATHを使う方法もあります。(未検証)
5. アプリケーションを実行する
上記の状態でアプリケーションを実行することで、新しいCUDAを利用することができます。
nvidia-smi
具体例: nvidia-smi
コマンドを使うことで、手軽に動作を確認することができます。
まず、LD_LIBRARY_PATH
環境変数を設定していない状態で動作を確認します。
$ nvidia-smi | head -n 4
XXX XXX XX XX:XX:XX 2021
+-----------------------------------------------------------------------------+
| NVIDIA-SMI 418.67 Driver Version: 418.67 CUDA Version: 10.1 |
|-------------------------------+----------------------+----------------------+
続いて、LD_LIBRARY_PATH
環境変数を設定し、同じコマンドを実行します。
$ export LD_LIBRARY_PATH=/home/yuya_kato/NVIDIA-Linux-x86_64-460.73.01:${LD_LIBRARY_PATH}
$ nvidia-smi | head -n 4
XXX XXX XX XX:XX:XX 2021
+-----------------------------------------------------------------------------+
| NVIDIA-SMI 418.67 Driver Version: 418.67 CUDA Version: 11.2 |
|-------------------------------+----------------------+----------------------+
LD_LIBRARY_PATH
環境変数を設定することで、CUDAドライバAPIのバージョンが10.1
から11.2
に変化していることが確認できます。
具体例: PyTorch v1.9.0(CUDA 11.1)
その他の具体例として、CUDA 11.1を利用するPyTorch v1.9.0の例を示します。
$ export LD_LIBRARY_PATH=/home/yuya_kato/NVIDIA-Linux-x86_64-460.73.01:${LD_LIBRARY_PATH}
$ nvidia-smi | head -n 4
XXX XXX XX XX:XX:XX 2021
+-----------------------------------------------------------------------------+
| NVIDIA-SMI 418.67 Driver Version: 418.67 CUDA Version: 11.2 |
|-------------------------------+----------------------+----------------------+
$ python3
Python 3.6.13 (default, Jun 29 2021, 17:11:32)
[GCC 4.8.5 20150623 (Red Hat 4.8.5-16)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import torch
>>> torch.__version__
'1.9.0+cu111'
>>> torch.cuda.is_available()
True
>>> torch.cuda.device_count()
1
>>> torch.cuda.get_device_name(0)
'Tesla V100-PCIE-16GB'
>>> device = torch.device("cuda:0")
>>> one = torch.Tensor([1.0]).to(device)
>>> two = torch.Tensor([2.0]).to(device)
>>> one + two
tensor([3.], device='cuda:0')
本来、CUDA 11.1の動作には450.80.02
以上のNVIDIAドライバが必要ですが、418.67
の環境で動作しています。
LD_LIBRARY_PATH
環境変数を設定しない場合(CUDAドライバAPIのバージョンが10.1
の場合)、以下のようなエラーメッセージが出力されます。
$ python3
Python 3.6.13 (default, Jun 29 2021, 17:11:32)
[GCC 4.8.5 20150623 (Red Hat 4.8.5-16)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import torch
>>> torch.__version__
'1.9.0+cu111'
>>> torch.cuda.is_available()
/home/yuya_kato/work/pyenv-2.0.1/versions/3.6.13/lib/python3.6/site-packages/torch/cuda/__init__.py:52: UserWarning: CUDA initialization: CUDA driver initialization failed, you might not have a CUDA gpu. (Triggered internally at /pytorch/c10/cuda/CUDAFunctions.cpp:115.)
return torch._C._cuda_getDeviceCount() > 0
False
最後に
データセンタ向けGPU限定、かつLinux環境限定ではありますが、「Forward Compatible Upgrade」を使うことで、NVIDIAドライバとCUDAのバージョンの呪縛から幾分開放されました。
さらに、NVIDIA Docker(NVIDIA Container Toolkit)を組み合わせることで、CUDAツールキットのバージョン、Linuxディストリビューションの自由度も高まります。
いつの日か、コンシューマ向けのGPUでもサポートされると良いですね。
Discussion