本文介绍如何在 Linux 虚拟机中直接使用 GPU 做科学计算,要达到这个目的,需要满足下面几个条件:

  1. 物理主机使用 VMWare ESXi 作为虚拟化的 VMM,并且版本最好大于等于 6.5
  2. 使用的是 Nvidia GPU 的显卡
  3. Linux 虚拟机 OS 没有限制,我使用的是 ubuntu

ESXi 开启显卡直通

假设已经安装好了 ESXi,通过 WebUI 进入 Host 的 Manage 界面,点击 Hardware,如图

esxi1

把 nVidia 开头的这几个全部选中,然后 “Active”, 表示开启 PCI 设备的直通 (passthrough)。

重启物理主机。

配置虚拟机

创建一个新虚拟机,或者修改已有的虚拟机,

点击 Edit,VM Options ,在 Advanced 里面点击 Edit configuration

esxi2

增加一条配置参数 hypervisor.cpuid.v0, 对应的值为 FALSE,这一步的目的是让驱动把虚拟机当做物理机来处理。

另一需要修改的地方让虚拟机硬件配置内存大小下面勾选 “Reserve all guest memory (All locked)”,让虚拟机启动时一次性获取物理主机内存,而不是按需获取。

到这里,主机和虚拟机的配置就全部完成了,接下来是驱动软件的安装

虚拟机安装驱动

重启并进入虚拟机 CLI,首先可以确认一下 GPU 已经被直通给了虚拟机,这一步不是必须要做,但检查一下没坏处。

$ lshw | grep display

$ sudo apt install ubuntu-drivers-common
$ ubuntu-drivers devices

接下来,禁用系统自带的开源 nouveau 驱动

$ sudo apt install gcc g++ make
$ sudo bash -c "echo blacklist nouveau > /etc/modprobe.d/blacklist-nvidia-nouveau.conf"
$ sudo bash -c "echo options nouveau modeset=0 >> /etc/modprobe.d/blacklist-nvidia-nouveau.conf"
$ cat /etc/modprobe.d/blacklist-nvidia-nouveau.conf

然后去 nvidia 官网下载对应的显卡驱动 https://www.nvidia.com/Download/index.aspx

安装驱动

$ chmod +x NVIDIA-Linux-x86_64-440.82.run
$ sudo ./NVIDIA-Linux-x86_64-440.82.run

重启虚拟机之后,运行 nvidia-smi 命令,出现类似如下结果说明安装成功

$ nvidia-smi
+-----------------------------------------------------------------------------+
| NVIDIA-SMI 418.88       Driver Version: 418.88       CUDA Version: 10.1     |
|-------------------------------+----------------------+----------------------+
| GPU  Name        Persistence-M| Bus-Id        Disp.A | Volatile Uncorr. ECC |
| Fan  Temp  Perf  Pwr:Usage/Cap|         Memory-Usage | GPU-Util  Compute M. |
|===============================+======================+======================|
|   7  TITAN RTX           On   | 00000000:B2:00.0 Off |                  N/A |
| 46%   66C    P2   215W / 280W |  23509MiB / 24190MiB |     65%      Default |
+-------------------------------+----------------------+----------------------+

对比 CPU 和 GPU 的运算速度

安装 docker

sudo apt install apt-transport-https ca-certificates curl software-properties-common
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu bionic stable"

sudo apt update

sudo apt install docker-ce

sudo usermod -aG docker ${USER}

安装 nvidia docker

$ curl -s -L https://nvidia.github.io/nvidia-docker/gpgkey |   sudo apt-key add -
$ curl -s -L https://nvidia.github.io/nvidia-docker/ubuntu16.04/amd64/nvidia-docker.list |   sudo tee /etc/apt/sources.list.d/nvidia-docker.list
$ sudo apt-get update
$ sudo apt-get install -y nvidia-docker2

$ sudo pkill -SIGHUP dockerd

$ docker run --runtime=nvidia --rm nvidia/cuda nvidia-smi

如果最后一条命令输出结果和之前的 nvidia-smi 一样,则说明在 docker container 可以使用 GPU 计算了。

除了指定 runtime 参数以外,我们也可以直接用 nvidia-docker 命令代替原来的 docker 命令。

拉取 docker image

看到这里,小朋友你们是不是有很多问号? 说好的对比 CPU 和 GPU 速度为什么要安装 docker 呢?

因为我懒得去搭建使用 GPU 做科学计算的各种环境,pull 一个 docker image 是最省力的方法。当然,你们也可以直接在 VM 中安装各种机器学习的 python 包。

首先,pull 一个 TensorFlow image

docker pull  gcr.io/kubeflow-images-public/tensorflow-2.1.0-notebook-gpu:1.0.0

然后运行并进入这个 container 的命令行

sudo nvidia-docker run --rm -it -p 8000:8888 -e GRANT_SUDO=yes -u root gcr.io/kubeflow-images-public/tensorflow-2.1.0-notebook-gpu:1.0.0 bash

命令行类似于下面这样

_______                               _______________
___  __/__________________________________  ____/__  /________      __
__  /  _  _ \_  __ \_  ___/  __ \_  ___/_  /_   __  /_  __ \_ | /| / /
_  /   /  __/  / / /(__  )/ /_/ /  /   _  __/   _  / / /_/ /_ |/ |/ /
/_/    \___//_/ /_//____/ \____//_/    /_/      /_/  \____/____/|__/


tf-docker /tf >

最后,直接用 python 运行下面这段代码

import tensorflow as tf
import time

def matmul(device_str):
    a = tf.random.normal((20000, 8000))
    b = tf.random.normal((8000, 8000))
    with tf.device(device_str):
        c = tf.matmul(a,b)

    return c

g = tf.Graph()
with g.as_default():
    cpu_c = matmul("/cpu:0")
    gpu_c = matmul("/gpu:0")
    init = tf.compat.v1.global_variables_initializer()

with tf.compat.v1.Session(graph=g) as sess:
    sess.run(init)
    for i in range(50):
        print("======== {} =======".format(i))
        start = time.time()
        sess.run(cpu_c)
        end = time.time()
        print("cpu time: {}".format(end-start))

        start = time.time()
        sess.run(gpu_c)
        end = time.time()
        print("gpu time: {}".format(end-start))

它会打印出 CPU 和 GPU 分别使用了多少时间。

在运行期间,我们也可以看到 python 程序占用了 GPU

$ nvidia-smi
+-----------------------------------------------------------------------------+
| NVIDIA-SMI 418.88       Driver Version: 418.88       CUDA Version: 10.1     |
|-------------------------------+----------------------+----------------------+
| GPU  Name        Persistence-M| Bus-Id        Disp.A | Volatile Uncorr. ECC |
| Fan  Temp  Perf  Pwr:Usage/Cap|         Memory-Usage | GPU-Util  Compute M. |
|===============================+======================+======================|
|   7  TITAN RTX           On   | 00000000:B2:00.0 Off |                  N/A |
| 46%   66C    P2   215W / 280W |  23509MiB / 24190MiB |     65%      Default |
+-------------------------------+----------------------+----------------------+
+-----------------------------------------------------------------------------+
| Processes:                                                       GPU Memory |
|  GPU       PID   Type   Process name                             Usage      |
|=============================================================================|
|    7     42352      C   python                                     23499MiB |
+-----------------------------------------------------------------------------+

在我自己的机器上,GPU 比 CPU 快 10 倍左右。

这是因为 GPU 天然适合做矩阵运算,所以耗时会有明显的差异,如果换成其他的计算,结果可能就是 CPU 更快了。

(完)