问题

在服务器里使用显卡跑程序的时候,经常有新手甚至老手也不太清除为什么程序跑起来了,但是感觉跑的很慢,调用了GPU好像没有调用成功一样,明明程序成功运行了。

如上图,PID 为28329进程运行程序时使用的 GPU 默认占满所有可用的显卡 GPU 和 G_MEM,但只在第一个 GPU 上进行计算。虽然调用了八块显卡资源,并且占用了所有 GPU 剩下的显存,但却只有一块真正在工作,大大浪费了资源,利用率低下。

解决方法很简单,TensorFlow 有一些参数可以简单控制显卡调用数量,可以通过在运行代码前添加参数,如:

调用第 1 块和第 5 块显卡的资源进行运算。

但是!指定调用了显卡的 GPU 还是会完全占用指定显卡的所有显存。如上图第二号显卡,已经有别的人使用了 1329 MB,但 28329 进程很霸道的把显卡剩下的 10625 MB显存全部划走,明明都用不上!

为了不占用显卡所有的显存,还可以在创建 sess 的时候,添加参数约束调用显卡的显存量,如:

可是!实际上这只是解决了单 GPU 调用问题,用了合理的资源去运行我的程序而已。如果我需要用多个 GPU 去加速程序;或者运行程序需要多个显卡的 G_MEM 才能满足的时候,为了让程序可以顺利的调用多个 GPU,就需要从代码层面解决。

与 TensorFlow 的多 GPU 调用问题相比,Caffe 的多 GPU 调用问题就很简单了。Caffe 的单 GPU 切换到多 GPU 根本不需要修改任何代码,只需要在编译选项中吧 USE_NCCL 打开就可以了,剩下的都是 Caffe 自动完成。

但 TensorFlow 不一样,需要对代码有一定程度的了解,并且使用 tf.device(“/gpu:0”) 之类的代码包起来,还要注意各种变量存放的地方,各种操心,如变量放哪,怎么平均梯度,这需要对训练的过程代码非常熟悉。

  • “/cpu:0”                               :机器的 CPU
  • “/device:GPU:0”                :机器的 GPU 如果只有一个
  • “/device:GPU:1”                 :机器的第二个 GPU

TensorFlow 自己也有一些多 GPU 的示例,tutorials/image/cifar10/cifar10_multi_gpu_train.py,但也仅仅是一个 DEMO 而已,没有解释含义,换成自己的模型训练算法代码就不一定能跑起来了。

解决法:单GPU代码转为多GPU代码

利用 CUDA_VISIBLE_DEVICES 之类的方法确实可以解决一定程度的问题,但精细下来,还是会有不少的性能缺失。例如多个用户公用几块卡的时候,每个人分配不同的卡来做实验,或者每张卡上运行不同参数设置的程序(!!!),来获得线性加速比,以便在更短的时间内得到计算结果。这个时候,上面都方法就无能为力了,必须从代码层面解决了,就算可以用工程能力设置每个用户的调用量之类的方式约束,也只是会得到用户运行时因为各种资源不足而使得程序报错。(当然,貌似用集群的方法可以治一下。)

一个 MNIST 的例子:

以上面的 MNIST 为例子,对其进行多 GPU 化改造。

多 GPU 并行可分为模型并行数据并行两大类,我们经常用到的是数据并行的方式,而数据并行又可分为同步方式异步方式,由于一般都会配置同样的显卡,因此这里用同步的方式,即将数据分给不同的卡,等所有的 GPU 都计算完梯度后进行平均,最后再更新梯度。

数据并行原理:模型参数保存在一个指定 GPU/CPU 上,模型参数的副本在不同 GPU 上,每次训练,提供 batch_size * gpu_num 数据,并等量拆分成多个 batch,分别送入不同的 GPU。

首先要改造的是数据读取部分,由于用到多块卡,每张卡要分到不同的数据,所以在获取 batch 的时候要把大小改为:

一次性取足够的数据保证每块卡都分到 batch_size 大小的数据。然后,对取到的数据进行切分。

为了防止名字混乱,最好结合 name_scope 参数区分:

下面是一个全代码版本模式:

加上前面的快捷方法,欢喜的敲入运行指令:

之后,,,,,

又双叒叕出现问题了!!!!

调用多 GPU 跑的程序竟然还比调用单 GPU 的时候跑的程序用的时间还要长!!!

还是搞不懂,等待未来的我填坑,未完待续。。。