待ってました CUDA on WSL 2

# 最新情報 (2022-06-15 時点)

エヌビディアの佐々木です。

2020 年 6 月にこの記事を書いて以来、Windows Insider Preview や NVIDIA ドライバの新しいビルドが出たタイミングなどで記事を更新してきましたが、あちこちに注釈が増えて読みづらくなってきたので、今後はこの「最新情報」セクションに新しい情報を集約しようと思います。あと、更新履歴は記事末尾へ送りました。

## 私が動作を確認したバージョン

- Windows のビルド: [22621.1](https://blogs.windows.com/windows-insider/2022/05/11/announcing-windows-11-insider-preview-build-22621/) (Windows 11 バージョン 22H2)
- NVIDIA ドライバ: 512.95
  - nvidia-smi コマンドの GPU-Util 欄が "N/A" になってしまう問題は解決！
- 実行した NGC コンテナイメージ
  - [nvcr.io/nvidia/tensorflow:22.05-tf2-py3](https://ngc.nvidia.com/catalog/containers/nvidia:tensorflow)

## Docker Desktop for Windows の WSL 2 バックエンドについて

[ユーザーガイド](https://docs.nvidia.com/cuda/wsl-user-guide/index.html#support-matrix-for-wsl2)に下記のようにあるとおり、Docker Desktop がサポートされました。
> CLI and Docker Desktop Supported.

## ソフトウェアのサポート状況

こちらも合わせてご覧ください: [CUDA on WSL User Guide の NVIDIA Compute Software Support on WSL 2](https://docs.nvidia.com/cuda/wsl-user-guide/index.html#support-matrix-for-wsl2)


# ここから本編

※ ここからは、2020 年 6 月時点の古い内容が部分的に残っています。

[2020 年 5 月の BUILD カンファレンスで発表](https://devblogs.microsoft.com/commandline/the-windows-subsystem-for-linux-build-2020-summary/)されたとおり、WSL 2 で CUDA が使えるようになったので、早速試してみました。手順は簡単で、私がこんな駄文にまとめる意味もないくらいですが、マイクロソフトとエヌビディア双方にドキュメントが分散していますので、かつてはマイクロソフトで Services for UNIX (SFU) / Subsystem for UNIX-based Applications (SUA)  を、今はエヌビディアで GPU を売っている私が何か書いてみるのは一つの役割と言えましょう。え、そうでもない？

# CUDA on WSL 2 の環境構築 (Windows 側)

では、さっさと環境を作ってしまいましょう。手順の概略は [CUDA on WSL User Guide](https://docs.nvidia.com/cuda/wsl-user-guide/index.html#getting-started) に載っています。私が試した下記の内容もこのガイドに沿っています。

## [Windows 10 Insider Preview Build 20150 (以降) のインストール](https://docs.nvidia.com/cuda/wsl-user-guide/index.html#installing-wip)

※ Windows 11 を使う場合、Windows Insider Preview のビルドは不要です。このセクションはスキップして、ドライバのインストールへ進んでください。

良く訓練された Windows ユーザーなら、Insider Preview の Fast Ring (改め、Dev Channel) に参加しているはずなので、今朝 (日本では 2020 年 6月 18 日) 目が覚めたらもう [Build 20150.1000](https://blogs.windows.com/windowsexperience/2020/06/17/announcing-windows-10-insider-preview-build-20150/) が降ってきていたはずです。インストールしましょう。

※ Insider Preview はちょっとなぁ…という方、是非「[Boot from VHD 機能で Windows Insider Preview を安全に試す](https://qiita.com/ksasaki/items/4e802da17f51d602b17d)」もご覧ください。

Insider Preview に参加していないユーザーにとっては、これに参加してインストールするまでが一番時間のかかるプロセスかもしれません。下図のように、Windows 10の「設定」から Windows Insider Preview の「ファスト」に参加してください。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/32712/880f4be4-1746-4008-eda6-d804543fca54.png"/ width="50%">

ダウンロードとインストールにはそこそこ長い時間がかかります。どうにかそれが終わったら、コマンドプロンプトで ver コマンドを実行し、確かに 20150 (以降) がインストールされていることを確認しましょう。

```
C:\WINDOWS\system32>ver

Microsoft Windows [Version 10.0.21301.1000]
```

## [NVIDIA ドライバのインストール](https://docs.nvidia.com/cuda/wsl-user-guide/index.html#installing-nvidia-drivers)

ホスト側 Windows の準備ができたら、次に NVIDIA ドライバをインストールします。以前は、プレビュー版のドライバが必要でしたが、ユーザーガイドに

> R495 and later windows will have CUDA support for WSL 2.

とあるように、現在は通常の Windows 用ドライバで OK です。なお、このドライバ、WSL 2 の Linux カーネル側 **ではなく**、ホストの Windows にインストールします。

インストール完了後は、Windows の再起動が必要です。

## [WSL 2 のインストール](https://docs.nvidia.com/cuda/wsl-user-guide/index.html#installing-wsl2)

以前はここで、「『仮想マシン プラットフォーム』の有効化」と「Linux ディストリビューションのインストール」という手順を紹介していましたが、ビルド 20262 以降では、WSL 2 のインストールが非常に簡単になっています。

例えば、Ubuntu-18.04 をインストールするのであれば、管理者権限で起動した Power Shell かコマンド プロンプトで、次のコマンドを実行するだけです。

```
wsl --install -d Ubuntu-18.04
```

このコマンドで、以下の作業が実行されます。

- 「Linux 用 Windows サブシステム (WSL)」および「仮想マシン プラットフォーム」を有効にする
- 最新の Linux カーネルをダウンロードしてインストールする
- WSL 2 を既定値として設定する
- 指定の Linux ディストリビューション (この場合 Ubuntu 18.04) をダウンロードしてインストールする

Windows 側での作業は以上です。これで、WSL 2 が使えるようになりました。Windows のスタートメニューから、先ほどインストールした Linux ディストリビューションのアイコンをクリックするなどして、シェルを起動すればそこはおなじみの Linux 環境です。

あとは通常の Linux 環境と (おおむね) 同様に GPU を使えるのですが、下記のうちどちらの方法を使うかで必要な準備が変わってきますので、簡単に説明します。

- WSL 2 上で直接 CUDA プログラムを実行する。
- WSL 2 上で Docker と NVIDIA Container Toolkit を使って Docker コンテナで GPU を使う。

# [CUDA Toolkit のインストール](https://docs.nvidia.com/cuda/wsl-user-guide/index.html#running-cuda)

コンテナを使わずに、直接 CUDA プログラムを実行する場合は、WSL 2 上の Linux に CUDA Toolkit をインストールすれば良いのですが、重要な注意点があります。

**Linux 用の NVIDIA ドライバをインストールしてはいけません**

つい、いつもの癖で ```sudo apt-get install cuda``` などとやってしまいそうになりますが、 ```cuda-toolkit-<version>``` パッケージを指定してください。Ubuntu-18.04 に CUDA-11.2 をインストールする例は次の通りです。

``` bash
sudo apt-key adv --fetch-keys http://developer.download.nvidia.com/compute/cuda/repos/ubuntu1804/x86_64/7fa2af80.pub
sudo sh -c 'echo "deb http://developer.download.nvidia.com/compute/cuda/repos/ubuntu1804/x86_64 /" > /etc/apt/sources.list.d/cuda.list'
sudo apt-get update
sudo apt-get install -y cuda-toolkit-11-2
```

なお、CUDA Toolkit のバージョン 11.1 からは、ディストリビューションの選択肢に "WSL-Ubuntu" が増えています。WSL 2 で Ubuntu を利用する場合は、こちらを選択しても OK です。この場合、ネイティブ Linux と同様に "cuda" パッケージをインストールすれば OK です。

CUDA 11.4.1 で、ディストリビューションとして "WSL-Ubuntu" を、Installer Type に "dev (local)" を指定した場合のコマンドは次の通りです。

``` bash
wget https://developer.download.nvidia.com/compute/cuda/repos/wsl-ubuntu/x86_64/cuda-wsl-ubuntu.pin
sudo mv cuda-wsl-ubuntu.pin /etc/apt/preferences.d/cuda-repository-pin-600
wget https://developer.download.nvidia.com/compute/cuda/11.4.1/local_installers/cuda-repo-wsl-ubuntu-11-4-local_11.4.1-1_amd64.deb
sudo dpkg -i cuda-repo-wsl-ubuntu-11-4-local_11.4.1-1_amd64.deb
sudo apt-key add /var/cuda-repo-wsl-ubuntu-11-4-local/7fa2af80.pub
sudo apt-get update
sudo apt-get -y install cuda
```

CUDA Toolkit のインストール完了後は、動作確認の意味で /usr/local/cuda/samples 配下にあるサンプル プログラムをビルド・実行してみるのも良いでしょう。下図は、私の PC で deviceQuery プログラムを実行してみたところです。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/32712/45c46e4b-e239-15bf-cb23-836c57a9a8c5.png"/ width=50%>

ちゃんと認識されていますね！

# [コンテナー実行環境の構築](https://docs.nvidia.com/cuda/wsl-user-guide/index.html#setting-containers)

そもそも私が CUDA on WSL 2 を待っていたのは、[NGC](https://ngc.nvidia.com/) のコンテナーイメージを Windows 上で手軽に実行したいからです。普段使っている PC は Windows 10 と Ubuntuのデュアルブートにしてあるのですが、コンテナーで GPU を使うために Ubuntu にブートし直すのは面倒だなぁ、ということが結構あるわけです。

というわけで WSL 2 でのコンテナ実行環境を構築しますが、現時点では方法が二つあります。

- WSL 2 上の Linux に Docker と NVIDIA Container Toolkit をインストールする
- Docker Desktop とその WSL 2 バックエンドを使う

## Docker Desktop とその WSL 2 バックエンドを使う方法

以前から Docker Desktop をご利用の方には、WSL 2 上の Linux に Docker をインストールするよりも、こちらの方法が簡単だと思います。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/32712/f3270314-9adb-7b84-88a2-f79f9aa9c135.png" width="50%" />

ご参考: [Docker Desktop の公式ドキュメント](https://docs.docker.com/docker-for-windows/wsl/#install)


## WSL 2 上の Linux への Docker のインストール

※ Docker Desktop WSL 2 バックエンドを使う場合はこの操作は不要です。[NGC のコンテナーイメージを動かしてみる](https://qiita.com/ksasaki/items/ee864abd74f95fea1efa#ngc-%E3%81%AE%E3%82%B3%E3%83%B3%E3%83%86%E3%83%8A%E3%83%BC%E3%82%A4%E3%83%A1%E3%83%BC%E3%82%B8%E3%82%92%E5%8B%95%E3%81%8B%E3%81%97%E3%81%A6%E3%81%BF%E3%82%8B)へ進んでください。

[Docker 公式の便利スクリプト](https://docs.docker.com/engine/install/ubuntu/#install-using-the-convenience-script)で、最新の安定版をすぐにインストールできます。

``` bash
curl https://get.docker.com | sh
```

この時、

> WSL DETECTED: We recommend using Docker Desktop for Windows.

と警告されますが、このメッセージは無視して進めてください。

## WSL 2 上の Linux への NVIDIA Container Toolkit のイントール

※ Docker Desktop WSL 2 バックエンドを使う場合はこの操作は不要です。[NGC のコンテナーイメージを動かしてみる](https://qiita.com/ksasaki/items/ee864abd74f95fea1efa#ngc-%E3%81%AE%E3%82%B3%E3%83%B3%E3%83%86%E3%83%8A%E3%83%BC%E3%82%A4%E3%83%A1%E3%83%BC%E3%82%B8%E3%82%92%E5%8B%95%E3%81%8B%E3%81%97%E3%81%A6%E3%81%BF%E3%82%8B)へ進んでください。

ユーザーガイドにあるコマンドを一つずつ打つのもダルいので、次のようなシェルスクリプトにして実行しました。

``` bash
#!/bin/sh

distribution=$(. /etc/os-release;echo $ID$VERSION_ID)
curl -s -L https://nvidia.github.io/nvidia-docker/gpgkey | sudo apt-key add -
curl -s -L https://nvidia.github.io/nvidia-docker/$distribution/nvidia-docker.list | sudo tee /etc/apt/sources.list.d/nvidia-docker.list
curl -s -L https://nvidia.github.io/libnvidia-container/experimental/$distribution/libnvidia-container-experimental.list | sudo tee /etc/apt/sources.list.d/libnvidia-container-experimental.list

sudo apt-get update
sudo apt-get install -y nvidia-docker2
sudo usermod -aG docker $USER
```

最後に、Docker デーモンを再起動しておきましょう。

```
sudo service docker restart
```

以上で Linux 側の作業は終了です。ドライバーをインストールする作業がない分、通常の Linux で GPU 対応コンテナ環境をセットアップするよりも簡単なぐらいですね。

なお、ここでインストールした NVIDIA Container Toolkit は、かつて NVIDIA Docker と呼ばれていたもので、コンテナー内で GPU を利用するために必要なモジュールです。詳しくは[こちらの記事](https://medium.com/nvidiajapan/nvidia-docker-%E3%81%A3%E3%81%A6%E4%BB%8A%E3%81%A9%E3%81%86%E3%81%AA%E3%81%A3%E3%81%A6%E3%82%8B%E3%81%AE-20-09-%E7%89%88-558fae883f44)をご覧ください。

## NGC のコンテナーイメージを動かしてみる

早速 NGC のコンテナイメージを試してみましょう。私の PC には Ampere アーキテクチャの GPU が載っていますので、その Tensor コアを活用すべく、以前「[NGC のコンテナーイメージで AMP を試してみる](https://qiita.com/ksasaki/items/52a829b63c8deb022c50#ngc-%E3%81%AE%E3%82%B3%E3%83%B3%E3%83%86%E3%83%8A%E3%83%BC%E3%82%A4%E3%83%A1%E3%83%BC%E3%82%B8%E3%81%A7-amp-%E3%82%92%E8%A9%A6%E3%81%97%E3%81%A6%E3%81%BF%E3%82%8B)」に書いた TensorFlow イメージを動かしてみました。

WSL 2 の Ubuntu を起動し、次のコマンドを実行してコンテナを起動します。

``` bash
docker run --shm-size=1g --ulimit memlock=-1 --ulimit stack=67108864 --gpus all --rm -it nvcr.io/nvidia/tensorflow:22.05-tf2-py3

```

コンテナが起動したら、ResNet-50 をトレーニングベンチマークモード (学習データをその場で生成するモード) で実行します。AMP (Automatic Mixed Precision) を有効にするため、--precision=fp16 も付けてあります。

``` bash
/workspace/nvidia-examples/cnn/resnet.py \
  --export_dir=/tmp \
  --display_every=10 \
  --num_iter=100 \
  --iter_unit=batch \
  --batch_size=128 \
  --precision=fp16
```

下図の通り、普通に動きます。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/32712/cc8d7229-abf9-c74d-571e-268f1c345380.png" width="50%"/>

また、nvidia-smi コマンドでは、以前は "N/A" になっていた GPU 使用率が表示されるようになっています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/32712/30ec178f-9899-ab57-782d-db0faced6dd0.png)


※ なお、WSL は標準のコンソール以外にも様々なソフトウェアでアクセスできますが、この例で私が使っているのは [MobaXterm](https://mobaxterm.mobatek.net/) です。WSL に対応しているだけでなく、X サーバーも内蔵されている便利なやつです。リモートもローカルもこれ一つでOK.

# 「生の Linux」とは違うところも

先ほどの例のように、CUDA のサンプルや NGC のコンテナーイメージを動かすだけならあまり違和感もなく、別の Linux マシンにログインして動かしているのと区別が付かないぐらいです。しかし、通常の Linux とは異なる不思議な点もいくつかあります。

例えば、

`lspci` しても GPU は見当たらず…

```
# lspci
8375:00:00.0 SCSI storage controller: Red Hat, Inc. Virtio filesystem (rev 01)
86cb:00:00.0 SCSI storage controller: Red Hat, Inc. Virtio filesystem (rev 01)
a609:00:00.0 SCSI storage controller: Red Hat, Inc. Virtio filesystem (rev 01)
b246:00:00.0 SCSI storage controller: Red Hat, Inc. Virtio filesystem (rev 01)
b7b6:00:00.0 3D controller: Microsoft Corporation Device 008e
bd73:00:00.0 SCSI storage controller: Red Hat, Inc. Virtio filesystem (rev 01)
```

いつも存在するデバイスファイル等もありません。

```
# ls -l /dev/nvidia*
ls: cannot access '/dev/nvidia*': No such file or directory
# ls -l /proc/driver/nvidia
ls: cannot access '/proc/driver/nvidia': No such file or directory
```

そう、WSL 2 の Linux には、PCI デバイスとしての GPU は直接見えていません。NVIDIA ドライバを Windows にだけインストールしたことからもわかるように、GPU は Windows のデバイスとして管理されており、WSL 2 の Linux からは、それを /dev/dxg というデバイスを通じて利用します。この /dev/dxg は所謂準仮想化デバイスです。ホストの Windows と、WSL の軽量ユーティリティ VM は、VMBus という仮想バスで接続されており、Linux 側での /dev/dxg への操作は、 dxgkrnl という準仮想化ドライバによって VMBus 経由でホストの GPU へ伝わります。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/32712/84685cff-2d31-3b33-2513-94afe3b8fdae.png" width="50%" />
[Announcing CUDA on Windows Subsystem for Linux 2](https://devblogs.microsoft.com/directx/directx-heart-linux/) より

この、[ゲストの準仮想化デバイス]-[VMBus]-[ホストのデバイス] という仕組みは、Windows のハイパーバイザである Hyper-V が当初から備えているもので、ディスクや NIC のようなデバイスを完全にエミュレーションするのではなく、仮想環境に最適化した準仮想化デバイスとして実装するために利用されているものです。それが今回は、Windows の GPU を WSL 側で使うために活用されているわけですね。

※ ここで登場した Linux カーネル内の dxgkrnl は、従来から存在する Windows の DirectX グラフィックスカーネルと同じ名称ではありますが、新たに Linux 用に実装されたものだそうです。
ソースコードはこちら: [WSL2-Linux-Kernel/drivers/gpu/dxgkrnl/](https://github.com/microsoft/WSL2-Linux-Kernel/tree/linux-msft-wsl-4.19.y/drivers/gpu/dxgkrnl)

下記のように「Windows のとは共通点は何もない」と強調されていますね。IP 汚染の懸念はないよ、ということでしょう。
> Although they share a name, the version of dxgkrnl inside of the Linux kernel is a clean room implementation of a Linux GPU driver based on our GPU-PV protocol and doesn’t share anything else in common with its similarly named Windows counterpart. 

出典: [DirectX is coming to the Windows Subsystem for Linux](https://devblogs.microsoft.com/directx/directx-heart-linux/)

# [制限事項](https://docs.nvidia.com/cuda/wsl-user-guide/index.html#known-limitations-for-linux-cuda-apps)

以前に比べると、制限事項も減ってきました。[ユーザーガイド](https://docs.nvidia.com/cuda/wsl-user-guide/index.html#known-limitations-for-linux-cuda-apps)からいくつか抜き出してみます。

- Pascal より前の世代の GPU は非サポート (推奨はされないが、全く動かないわけではない)
- Unified Memory は部分的なサポートにとどまる。(これは WSL の制限と言うより、Windows 版ドライバの仕様)
- NVIDIA Container Toolkit では、docker run 時に --gpus all のみをサポート。複数の GPU がある場合に、一部に絞ってコンテナに使わせることができない。

などです。

# まとめ

 - Windows 10/11 の WSL 2 でついに CUDA が使えるようになりました。
 - NGC の TensorFlow コンテナイメージなどは既に普通に動きます。
 - nvidia-smi もちゃんと使えるようになりました。(GPU 使用率が表示されるようになりました)
 - まだ、いくつか制限事項もありますが、ぜひお試しの上フィードバックを。

お楽しみ CUDA さい！

# 関連情報

[Announcing CUDA on Windows Subsystem for Linux 2](https://devblogs.nvidia.com/announcing-cuda-on-windows-subsystem-for-linux-2/)
[CUDA on WSL User Guide](https://docs.nvidia.com/cuda/wsl-user-guide/index.html)
[NVIDIA Drivers for CUDA on WSL](https://developer.nvidia.com/cuda/wsl/download)
[Docker Desktop WSL 2 backend](https://docs.docker.com/docker-for-windows/wsl/)
[Announcing Windows 10 Insider Preview Build 20150](https://blogs.windows.com/windowsexperience/2020/06/17/announcing-windows-10-insider-preview-build-20150/)
[The Windows Subsystem for Linux BUILD 2020 Summary](https://devblogs.microsoft.com/commandline/the-windows-subsystem-for-linux-build-2020-summary/)
[Windows Subsystem for Linux Documentation](https://docs.microsoft.com/en-us/windows/wsl/)
[Boot from VHD 機能で Windows Insider Preview を安全に試す](https://qiita.com/ksasaki/items/4e802da17f51d602b17d)
[Automatic Mixed Precision (AMP) でニューラルネットワークのトレーニングを高速化](https://qiita.com/ksasaki/items/52a829b63c8deb022c50)
[NVIDIA Docker って今どうなってるの？ (20.09 版)](https://medium.com/nvidiajapan/nvidia-docker-%E3%81%A3%E3%81%A6%E4%BB%8A%E3%81%A9%E3%81%86%E3%81%AA%E3%81%A3%E3%81%A6%E3%82%8B%E3%81%AE-20-09-%E7%89%88-558fae883f44)

# 更新履歴
※ 2022/06/15 NVIDIA ドライバ 512.95 と Windows 11 [22621.1](https://blogs.windows.com/windows-insider/2022/05/11/announcing-windows-11-insider-preview-build-22621/) に関して追記しました。
※ 2021/10/12 Windows 11 の正式リリースに関して追記しました。
※ 2021/09/04 NVIDIA ドライバ 510.06 と、[WIP ビルド 22000.176](https://blogs.windows.com/windows-insider/2021/09/02/announcing-windows-11-insider-preview-build-22000-176/)に関して追記しました。
※ 2021/07/30 NVIDIA ドライバ 471.21 と、[WIP ビルド 22000.100](https://blogs.windows.com/windows-insider/2021/07/22/announcing-windows-11-insider-preview-build-22000-100/)に関して追記しました。
※ 2021/06/30 NVIDIA ドライバ 471.11 と、[WIP ビルド 22000.51](https://blogs.windows.com/windows-insider/2021/06/28/announcing-the-first-insider-preview-for-windows-11/)に関して追記しました。
※ 2021/06/07 NVIDIA ドライバ 470.76 と、[WIP ビルド 21390](https://blogs.windows.com/windows-insider/2021/05/26/announcing-windows-10-insider-preview-build-21390/)に関して追記しました。
※ 2021/04/08 NVIDIA ドライバ 470.14 で nvidia-smi が動作しない問題が報告されている点を追記しました。
※ 2021/03/12 Windows Insider Preview ビルド 21332 および NVIDIAドライバ 470.05 に合わせて更新しました。(更新履歴は記事末尾へ移しました)
※ 2021/02/05 Docker Desktop の WSL 2 バックエンドが GPU に対応した点を追記しました。
※ 2021/01/31 ビルド 21292 以降と NVIDIA ドライバ 465.42 で、CUDA 11.2 に対応、性能向上、NVML のサポート、nvidia-smi コマンドのサポートなど重要な改善があったので追記しました。
※ 2021/01/03 CUDA 11.2 で CUDA on WSL 2 がうまく動かない問題が確認されたこと等を追記しました。
※ 2020/10/15 CUDA Toolkit インストール時に "WSL-Ubuntu" が選択できるようになった点などを追記しました。
※ 2020/07/27 WSL 2 への CUDA Toolkit インストール時の注意点を追記しました。