最大化张量流多 GPU 性能

Maximize tensorflow multi gpu performance

本文关键字:性能 GPU 张量流 最大化      更新时间:2023-10-16

我想知道是否有人可以建议如何在 4 GPU 设置中从 tensorflow 中获得峰值性能。

作为测试,我在 32x32 输入上创建了两个相同的网络(18 层残差网络,带有小滤波器组(范围从 16-128)。批量大小 512,每个 GPU 128 个)。一个在MXNet中,一个是我模仿的初始示例。

我的MXNet网络每秒可以训练大约7k个示例,其中tensorflow只能使用虚拟数据进行4.2k,而使用真实数据只能训练3.7k。

(在 1 个 GPU 上运行时,数字是每秒 1.2k 个示例与 2.1k 相比)

在我的实验中,我有几个问题,希望能加快速度。

  1. 训练时 GPU 利用率似乎很低。我注意到在 tensorflow 白皮书中,支持在同一 GPU 上运行多个流。这在公开发布中可能吗?

  2. 无论如何可以在一次执行session.run()中执行多个火车操作吗?还是异步执行?这将允许在下一批转发传递的同时进行权重更新?我尝试使用 2 个线程(系统和 QueueRunners 的线程),但这只会导致速度变慢。MXNet能够通过在CPU上运行权重更新来提高速度,以便GPU可用于下一批。

  3. 新的分布式运行时是否会通过让我在一台计算机上运行多个工作线程来解决其中一些问题?

  4. 还有什么可以做的吗?

我知道这里有很多关于堆栈溢出的类似问题,但是尽管我的搜索我找不到我尚未尝试过的问题的解决方案。

编辑:

我做了一些 CUDA 分析,看看昂贵的内核是什么。根据我的跑步,21.4%的时间是在里面度过的:

void Eigen::internal::EigenMetaKernel_NonVectorizable<Eigen::TensorEvaluator
<Eigen::TensorAssignOp<Eigen::TensorMap<Eigen::Tensor<float, int=4, int=1, long>, int=16>,
Eigen::TensorPaddingOp<Eigen::array<std::pair<int, int>,
unsigned long=4> const, Eigen::TensorMap<Eigen::Tensor<float const,
int=4, int=1, long>, int=16> const > const > const, Eigen::GpuDevice>, long>(float, int=4)

20.0%的时间花在

void Eigen::internal::EigenMetaKernel_NonVectorizable<Eigen::TensorEvaluator
<Eigen::TensorAssignOp<Eigen::TensorMap<Eigen::Tensor<float, int=4, int=1, long>, int=16>,
Eigen::TensorBroadcastingOp<Eigen::array<int, unsigned long=4>
const, Eigen::TensorMap<Eigen::Tensor<float const, int=4, int=1, long>,
int=16> const > const > const, Eigen::GpuDevice>, long>(float, int=4)

离开签名,我不确定这些在做什么。这些有意义吗?

除此之外,分析报告内核并发性较低,如预期的那样为 0%。和低计算利用率 34.9%(当然这包括启动时间和训练循环中的一点 python。总共 32 秒左右,共 91 秒。这在张量流内部的利用率约为 50%。

编辑 2:

我附上了一份精简的源代码的副本。总的来说,虽然我更关心问题1-3,不想花太多时间。

此外,我在张量流上运行,构建自:f07234db2f7b316b08f7df25417245274b63342a

编辑3:

更新到最新的张量流(63409bd23facad471973b110df998782c0e19c06)相同的代码,默认数据格式(NHWC),这似乎大大加快了速度。在假数据 6.7k-6.8k(我认为是热依赖性?)上,第二个 4GPU 的例子。1GPU -- 每秒 2.0k 个示例。对于 4GPU 而言,实际数据性能约为每秒 4.9k 个示例。1GPU -- 每秒 1.7k 个示例。

编辑4:

此外,我尝试将数据格式切换到BCHW。我根据 Soumith 的基准进行了建模转换。卷积部分确实更快,但批量规范似乎把一切都搞砸了。使用朴素的实现(固定轴,并制作权重 [1,C,1,1] 而不是 [C,]),我只能在 1.2 个 gpu(假数据)上每秒获得 4k 个示例。与批处理规范操作之前和之后的转置一样,我每秒能够获得 6.2k 个示例(假数据)。仍然比NHWC data_format慢。

如果不查看代码,很难诊断程序的性能问题。我们是否可以以某种方式读取您的测试代码?

顶部显示的张量填充有点奇怪。我希望 cudnn 调用应该在配置文件的顶部。无论如何,向我们展示测试代码会有所帮助。