【问题标题】:Slicing a 300MB CuPy array is ~5x slower than NumPy切片 300MB CuPy 数组比 NumPy 慢约 5 倍
【发布时间】:2020-03-31 17:48:08
【问题描述】:

我的代码涉及对 432x432x400 数组进行总计约 1000 万次切片,以生成用于神经网络训练的批量数据。由于这些是相当大的数组(9200 万数据点/300MB),我希望使用 CuPy 来加快速度(甚至可能通过在与训练相同的 GPU 上生成数据来加速训练),但发现它实际上使代码慢 5 倍。

这是由于 CuPy 开销导致的预期行为还是我遗漏了什么?

要重现的代码:

import cupy as cp
import numpy as np
import timeit
cp_arr = cp.zeros((432, 432, 400), dtype=cp.float32)
np_arr = np.zeros((432, 432, 400), dtype=np.float32)

# numbers below are representative of my code
cp_code = 'arr2 = cp_arr[100:120, 100:120, 100:120]'
np_code = 'arr2 = np_arr[100:120, 100:120, 100:120]'

timeit.timeit(cp_code, number=8192*4, globals=globals())  # prints 0.122
timeit.timeit(np_code, number=8192*4, globals=globals())  # prints 0.027

设置:

  • GPU:NVIDIA Quadro P4000

  • CuPy 版本:7.3.0

  • 操作系统:CentOS Linux 7

  • CUDA 版本:10.1

  • cuDNN 版本:7.6.5

【问题讨论】:

    标签: cupy


    【解决方案1】:

    我还证实,cupy 中的切片速度大约慢了 5 倍,而有一种更精确的方法来测量时间(参见例如https://github.com/cupy/cupy/pull/2740)。

    数组的大小无关紧要,因为切片操作不会复制数据而是创建视图。与下面的结果类似。

    cp_arr = cp.zeros((4, 4, 4), dtype=cp.float32)
    cp_code = 'arr2 = cp_arr[1:3, 1:3, 1:3]'
    

    “获取切片然后将其发送到 GPU”自然会更快,因为它减少了要传输的字节数。如果第一个预处理是切片,请考虑这样做。

    【讨论】:

    • 谢谢,这是有道理的。显示CuPy website 上的切片加速因子高达 190 倍的图表让我希望获得一些好处。
    • @Elcor 上面提到的图表实际上是由我绘制的。它绝对不能代表我们可以进行切片的无限方式,而只是切片具有[::3] 模式的一种简单方式。绘制该图的确切操作是github.com/pentschev/pybench/blob/master/pybench/benchmarks/…
    【解决方案2】:

    NumPy 和 CuPy 中的切片实际上并不是将数据复制到任何地方,而是简单地返回一个新数组,其中数据相同,但其指针偏移到新切片的第一个元素和调整后的形状。请注意以下原始数组和切片如何具有相同的步幅:

    In [1]: import cupy as cp
    
    In [2]: a = cp.zeros((432, 432, 400), dtype=cp.float32)
    
    In [3]: b = a[100:120, 100:120, 100:120]
    
    In [4]: a.strides
    Out[4]: (691200, 1600, 4)
    
    In [5]: b.strides
    Out[5]: (691200, 1600, 4)
    

    上面的相同可以通过用 NumPy 替换 CuPy 来验证。

    如果您想为实际的切片操作计时,最可靠的方法是在每个操作中添加.copy(),从而强制执行内存访问/复制:

    cp_code = 'arr2 = cp_arr[100:120, 100:120, 100:120].copy()'  # 0.771 seconds
    np_code = 'arr2 = np_arr[100:120, 100:120, 100:120].copy()'  # 0.154 seconds
    

    不幸的是,对于上述情况,内存模式对 GPU 不利,因为小块无法使内存通道饱和,因此它仍然比 NumPy 慢。但是,如果块能够接近内存通道饱和,CuPy 会更快,例如:

    cp_code = 'arr2 = cp_arr[:, 100:120, 100:120].copy()'  # 0.786 seconds
    np_code = 'arr2 = np_arr[:, 100:120, 100:120].copy()'  # 2.911 seconds
    

    【讨论】:

      猜你喜欢
      • 2018-12-13
      • 2019-12-14
      • 2014-12-23
      • 2022-01-23
      • 1970-01-01
      • 1970-01-01
      • 2015-01-03
      • 2018-01-16
      • 2020-12-11
      相关资源
      最近更新 更多