Anchormen / wsl2-gpu

8 stars 3 forks source link

GPU support in WSL 2 in practice: Jupyter notebooks and Rstudio working seamlessly with a GPU using docker containers.

If you are reading this blog, probably you are wondering whether a GPU can be shared by Windows. This is a real problem because in virtual machines hosted on Windows, GPU support doesn’t work properly or it’s really hard to set up.

Even docker cannot use GPUs in Linux containers running in Windows as host. The only real solution if you want to run GPU-based code in a Linux container in Windows is literally getting rid of Windows; meaning, having a dual boot with a Linux distribution and run it there. If this is your case, you should continue reading this blog.

If you are already a WSL 2 user, additional improvements over the standard installation are described as automated docker initialization and Linux distros export to avoid reinstallation of standard programs.

Moreover, a docker image was developed with both Jupyter notebooks and Rstudio able to use Tensorflow and Keras with a GPU if available. Furthermore, it will be shown that both R and Python installations can be used seamlessly by both UIs without additional configuration including GPU support. Finally, some caveats will be described, such as GPU support not working for latest Windows insider build (20231) and a decrease in performance.

The custom Docker image and its Dockerfile can be found here and here respectively.

1. WSl 2 and docker in Windows 10.

Docker for windows is moving to a new architecture paradigm to improve the resource consumption, as described here. It’s called WSL 2, which is the abbreviation for Windows Subsystem for Linux. In general, it’s a lightweight virtual machine with a complete Linux kernel, which increases I/O performance. Now, resources sharing with the host Windows systems works seamlessly, especially file sharing. If you haven’t tried it yet, you should since it’s really easy to install and delivers exactly what it promises – a linux distribution within your Windows system with a simple integration. Moreover, Docker plans to use WSL 2 as primary engine in the short term and, in the future, the only way that Docker Desktop will be deployed.

2. GPU compute support in Windows.

When GPU support is needed for ML/AI the first decision that should be made is the environment. In my opinion, using the GPU directly on the host is dangerous. I already had some bad experiences trying to recreate the environment in another computer, for example, a server in the cloud. Because of that, I prefer dockerizing and keeping the environment under control. Until now, I could only use Linux to develop/test these environments. Since I use also Windows, the only solution was to use dual boot with Linux/Windows because there is no other way to run Linux GPU based containers in Windows. VMs doesn’t have native GPU support or is really hard to set up and Docker for Windows simply did not allow it as nvidia-docker was only supported for Linux. In May this year, Windows announced that WSL 2 will support GPU Computes and Ubuntu itself released on June a guide to run Jupyter Notebook in Docker with CUDA support in Windows using WSL 2 (see here).

3. WSL 2 configuration to include GPU support.

3.1. Windows version.

As of the time of writing this blog, GPU support works ONLY using the Official Windows 10 client Insider build 20201. You can download it from here, and follow the steps provided here. Be aware that a Windows Insider program account is needed, check here for additional details. Check running ver in the command line. It should match Microsoft Windows [Version 10.0.20201.1000].

3.2. WSL 2 installation

WSL 2 installation should be straight forward following the official documentation here and GPU support here or here. For convenience, I’ve summarized it below for the windows command line:

  :: Enable WSL 2
  dism.exe /online /enable-feature /featurename:Microsoft-Windows-Subsystem-Linux /all /norestart
  dism.exe /online /enable-feature /featurename:VirtualMachinePlatform /all /norestart

Restart Windows 10 and run:

  :: Set WSL 2 as the default version
  wsl.exe --set-default-version 2

Then, install Ubuntu from the Microsoft Store as here and continue with the following commands:

  :: Update the WSL 2 Linux kernel
  wsl.exe --update
  wsl --shutdown

Finally, set up a new distro by running the newly installed Linux distribution and provide a user name and password.

To check whether the installation was correct, run wsl cat /proc/version in a windows command line or cat /proc/version inside WSL 2. The outcome should be the following:

  Linux version 4.19.128-microsoft-standard

3.3. Nvidia drivers

Go here and install the specific drivers required for your NVIDIA model.

3.4. Native docker for windows vs docker-ce in WSL2

The natural next step could be the installation of Docker Desktop. However, NVIDIA Container Toolkit does not yet support Docker Desktop WSL 2 backend. Check this out for additional details.

Because of that, standard Docker-CE should be installed. Run the following command INSIDE WSL 2 Ubuntu distribution:

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

Then, install NVIDIA Container Toolkit running the following commands in two steps. First, setup the stable and experimental repositories:

   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

and then, the NVIDIA runtime packages:

   sudo apt update && sudo apt install -y nvidia-docker2
   sudo usermod -a -G docker $USER

Finally, close the WSL2 window and run the next command in the windows CLI to restart WSL 2:

   wsl --shutdown Ubuntu

To check that everything was installed correctly, open a new WSL 2 window and execute the following command:

   sudo service docker start

Open an additional WSL 2 window and execute the following command:

   docker run --gpus all nvcr.io/nvidia/k8s/cuda-sample:nbody nbody -gpu -benchmark

The Nvidia graphic card should be automatically detected and it should show something similar to:

   > Windowed mode
   > Simulation data stored in video memory
   > Single precision floating point simulation
   > 1 Devices used for simulation
   MapSMtoCores for SM 7.5 is undefined.  Default to use 64 Cores/SM
   GPU Device 0: "GeForce RTX 2070" with compute capability 7.5

   > Compute 7.5 CUDA device: [GeForce RTX 2070]
   36864 bodies, total time for 10 iterations: 67.554 ms
   = 201.166 billion interactions per second
   = 4023.329 single-precision GFLOP/s at 20 flops per interaction

3.5. Automated docker initialization

Most of the WSL 2 installation description ends here. I thought I was done with it. However, the more I was using WSL 2, starting every time the Docker Daemon the more painful it was. Naturally, I started looking for an easier way around this. That’s when I found this excellent blog with a very elegant solution to this problem, summarized in the following lines.

Open a WSL 2 windows and run:

   sudo visudo

At the end of the file, add the following line changing <USER_NAME> with the name of your linux user:

   <USER_NAME> ALL=(ALL) NOPASSWD: /usr/bin/dockerd

In this way, your user can start the docker daemon without asking for the password. Save the modifications and run:

   echo '# Start Docker daemon automatically when logging in if not running.' >> ~/.bashrc
   echo 'RUNNING=`ps aux | grep dockerd | grep -v grep`' >> ~/.bashrc
   echo 'if [ -z "$RUNNING" ]; then' >> ~/.bashrc
   echo '    sudo dockerd > /dev/null 2>&1 &' >> ~/.bashrc
   echo '    disown' >> ~/.bashrc
   echo 'fi' >> ~/.bashrc

With this script added to the bashrc, WSL 2 will check if the docker daemon was already initiated every time you open a WSL 2 window. If not, Docker will be started.

To check whether this is working properly, just close all WSL 2 windows, run wsl --shutdown Ubuntu in the windows command line and open a new WSL 2 window.

The following command should run now without errors:

   docker run --gpus all nvcr.io/nvidia/k8s/cuda-sample:nbody nbody -gpu -benchmark

3.6. WSL2 distros export/import

Another interesting WSL 2 feature is the ability to export/import distros. If a brand new WSL 2 distro is needed, all the previous steps should be repeated. To avoid that, the WSL 2 distro can be exported and used as a “clean” template.

To do that run the following commands in the Windows command line, changing <PATH> with the path in which the distro export file should be copied, for example, C:\wslbackup:

   SET wsl2_export_path=<PATH>
   mkdir %wsl2_export_path%
   wsl --export Ubuntu %wsl2_export_path%\ubuntu.tar

From now on, this tar file can be used as a template. For that:

4. Integration of WSL2 with Jupyter notebooks and Rstudio including GPU support via docker images.

To show the possibilities of WSL 2 with GPU support, three use cases were deployed:

Figure 1

Figure 1. Running a Keras model in R using python Tensorflow backend

4.3. Jupyter notebook running R code.

Just as a bonus, R code can be run on a Jupyter notebook since the image has a functional R installation.

Open a new python 3 notebook and run the following code from here and here:

   import math, datetime
   import rpy2.robjects.lib.ggplot2 as ggplot2
   import rpy2.robjects as ro
   from rpy2.robjects.packages import importr, data
   from rpy2.ipython.ggplot import image_png
   base = importr('base')
   datasets = importr('datasets')
   mtcars = data(datasets).fetch('mtcars')['mtcars']
   pp = (ggplot2.ggplot(mtcars) +
         ggplot2.aes_string(x='wt', y='mpg', col='factor(cyl)') +
         ggplot2.geom_point() +
         ggplot2.geom_smooth(ggplot2.aes_string(group = 'cyl'),
                             method = 'lm'))

   image_png(pp)

The expected output is shown in the following figure:

Figure 1

Figure 2. Plotting a ggplot2 figure in a Jupyter Notebook using R backend

5. Caveats

  1. GPU support for WSL 2 is not officially released and it’s not working for the latest Windows Insider build – GPU support is the most wanted feature of WSL 2. However, it’s not fully implemented yet in regular updates. Even GPU support for the newest Windows 10 Insider build (20231) is not working and I had to install the previous build (20201) to make WSL 2 work with GPU compute support.

  2. Performance – Around 20% decrease in performance can be seen. Recently, NVIDIA updated the drivers for a higher performance, see more details here.

6. Conclusions

In this blog, we introduced WSL 2, which will be the default engine for Docker in Windows in the near future. Then, we summarized the necessary WSL2 configuration which is required for GPU support. We also developed a docker image able to run Jupyter notebooks and RStudio including GPU support. Using this image, Python and R installations are shared by both UIs. Finally, we gave examples of both combinations – R code within Jupyter notebooks (ggplot2 plot) and Python in Rstudio (Keras model).