摘要

使用 GPU 进行数组操作可以比 CPU 计算提供显著的加速,但加速程度很大程度上取决于具体操作。这篇博文旨在对 CuPy 在各种不同操作上的性能进行基准测试。我们当然可以引入 Dask 来实现多 GPU 性能提升,正如三月份的这篇博文所讨论的那样,但在这里,我们将只关注单 GPU CuPy 的个体性能。

硬件和软件设置

在进一步探讨之前,假设本文中描述的所有性能结果都使用了以下硬件和软件

  • 系统:NVIDIA DGX-1
  • CPU:2x Intel Xeon E5-2698 v4 @ 2.20GHz
  • 主内存:1 TB
  • GPU:NVIDIA Tesla V100 32 GB
  • Python 3.7.3
  • NumPy 1.16.4
  • Intel MKL 2019.4.243
  • CuPy 6.1.0
  • CUDA Toolkit 9.2(SVD 使用 10.1,请参阅“提升性能”部分)

总体性能

我们生成了一个包含各种操作的图表。其中大多数操作在使用 CuPy 的 GPU 上表现良好,无需额外配置。请看下面的图表

我们最近开始开发一个简单的基准测试套件,以帮助我快速可靠地进行基准测试,并自动化一些绘图工作。它目前尚不完善,缺乏文档,我们计划在接下来的几天里改进它。上面这张图表就是用它生成的。如果您有兴趣了解合成数据是如何精确生成的以及具体的计算操作是如何进行基准测试的,您可以查看这个文件。这篇博文不会详细介绍这个基准测试套件目前的工作原理,但我们计划在它更成熟、更容易使用后,在不久的将来写一篇关于它的文章。

从图表中可以看出,元素级操作可以获得 270 倍的加速。考虑到无需手动编写任何并行代码,这相当不错了。然而,加速效果受每种操作本身的特性影响巨大。在这篇博文中,我们不会深入探讨为什么每种操作的表现不同,但我们可能会在未来的博文中继续讨论。

让我简要描述一下上面图表中的每种操作

  • 元素级操作:对数组的所有元素执行标量操作
  • 求和:计算整个数组的总和,将其归约(reduce)为一个标量,使用了 CUB,目前仍在开发中
  • 标准差:计算整个数组的标准差,将其归约(reduce)为一个标量
  • 数组切片:选择第一维度的每第三个元素
  • 矩阵乘法:两个方阵的乘法
  • FFT:矩阵的快速傅里叶变换
  • SVD:矩阵的奇异值分解(对于较大的数组,使用“高瘦”矩阵)
  • Stencil(模板计算 - 非 CuPy 操作!):使用 Numba 进行均匀滤波

需要注意的是,这里有两种数组大小:800 MB 和 8 MB,前者指 10000x10000 的数组,后者指 1000x1000 的数组,两种情况下都使用双精度浮点数(8 字节)。SVD 的数组大小是例外,其中较大的大小实际上是一个“高瘦”矩阵,尺寸为 10000x1000,即 80MB。

提升性能

当我们第一次运行这些基准测试时,实际上在少数情况下看到了性能的下降

虽然 GPU 并非总是更快,但我们确实期望这些特定操作能比其 CPU 对应操作更快。这让我们感到困惑。

经过进一步调查,我们发现这两个问题要么已经被修复,要么正在由生态系统中的其他人积极修复。

  • SVD:CuPy 的 SVD 链接到官方的 cuSolver 库,该库在 CUDA 10.1 中对这类求解器进行了大幅度的性能提升(感谢 Joe Eaton 指出这一点!)。最初我们安装的是 CUDA 9.2,那时性能还慢很多。

    注意:上述大多数结果仍使用 CUDA 9.2。 只有 SVD 结果使用 CUDA 10.1。

  • 求和:CuPy 的求和代码确实相当慢。然而,Akira Naruse 已经提交了一个活跃的拉取请求,旨在利用用于 CUDA 的集合原语库 CUB 来加速这一操作。

通过进行这次基准测试,我们学到了很多。尤其令人欣慰的是,我们看到的性能问题已经由其他团队发现并纠正。这凸显了在开源社区中工作的好处之一:你无需自己完成所有工作,事情就能变得更快。

未来工作

为了更好地理解可扩展性,为各种其他大小生成基准测试结果会很有趣。如果有人没注意到,这次没有进行 Dask 的基准测试,这绝对也是需要做的事情!

拥有标准化的基准测试也很重要,基准测试套件应该得到改进,使其更通用,并且需要适当的文档。


博客评论由 Disqus 提供支持