将数组的列作为向量执行“for循环”时,Cupy 比 numpy 慢

Posted

技术标签:

【中文标题】将数组的列作为向量执行“for循环”时,Cupy 比 numpy 慢【英文标题】:Cupy slower than numpy when doing a "for loop" for columns of an array as vectors 【发布时间】:2019-08-18 14:53:21 【问题描述】:

我正在尝试使用 cupy 并行化以下操作: 我有一个数组。对于该数组的每一列,我生成 2 个随机向量。我取那个数组列,添加一个向量,减去另一个,然后使这个新向量成为数组的下一列。我继续,直到我完成阵列。

我已经问过以下问题 - Cupy slower than numpy when iterating through array。但这是不同的,因为我相信我遵循了并行化操作的建议,使用一个“for 循环”而不是两个,并且只遍历数组列而不是行和列。

import cupy as cp
import time
#import numpy as cp


def row_size(array):
    return(array.shape[1])

def number_of_rows(array):
    return(array.shape[0])

x = (cp.zeros((200,200), 'f'))
#x = cp.zeros((200,200))

x[:,1] = 500000

vector_one = x * 0
vector_two = x * 0

start = time.time()
for i in range(number_of_rows(x) - 1):
    if sum(x[ :, i])!=0:
        vector_one[ :, i + 1], vector_two[ :, i+ 1] = cp.random.poisson(.01*x[:,i],len(x[:,i])), cp.random.poisson(.01 * x[:,i],len(x[:,i]))
        x[ :, i+ 1] = x[ :, i] + vector_one[ :, i+ 1] - vector_two[ :, i+ 1]

 time = time.time() - start      
 print(x)
 print(time)

当我在 cupy 中运行它时,时间大约为 0.62 秒。

当我切换到 numpy 时,我 1) 取消注释 #import numpy as cp 和 #x = cp.zeros((200,200)) 和 2) 改为注释 import cupy as cp 和 x = (cp.zeros((200,200), 'f')):

时间大约为 0.11 秒。

我想也许如果我增加数组大小,例如从 (200,200) 到 (2000,2000),那么我会看到 Cupy 的速度有所不同,但它仍然更慢。

我知道这在某种意义上可以正常工作,因为如果我将 cp.random.poisson 中的系数从 0.01 更改为 0.5,我只能在 cupy 中这样做,因为 lambda 对于 numpy 来说太大了。

但是,我如何使用 cupy 让它变得更快?

【问题讨论】:

【参考方案1】:

一般来说,在主机 (CPU) 上循环并迭代处理小型设备 (GPU) 阵列并不理想,因为与面向列的方法相比,您必须启动更多的单独内核。但是,有时面向列的方法是不可行的。

您可以使用 CuPy 的 sum 来加速您的 CuPy 代码,而不是使用 Python 的内置 sum 操作,这会在您每次调用它时强制设备进行主机传输。话虽如此,您还可以通过切换到 NumPy 的 sum 来加速您的 NumPy 代码。

import cupy as cp
import time
#import numpy as cp


def row_size(array):
    return(array.shape[1])

def number_of_rows(array):
    return(array.shape[0])

x = (cp.zeros((200,200), 'f'))
#x = cp.zeros((200,200))

x[:,1] = 500000

vector_one = x * 0
vector_two = x * 0

start = time.time()
for i in range(number_of_rows(x) - 1):
#     if sum(x[ :, i]) !=0:
    if x[ :, i].sum() !=0: # or you could do: if x[ :, i].sum().get() !=0:
        vector_one[ :, i + 1], vector_two[ :, i+ 1] = cp.random.poisson(.01*x[:,i],len(x[:,i])), cp.random.poisson(.01 * x[:,i],len(x[:,i]))
        x[ :, i+ 1] = x[ :, i] + vector_one[ :, i+ 1] - vector_two[ :, i+ 1]

cp.cuda.Device().synchronize() # CuPy is asynchronous, but this doesn't really affect the timing here.

t = time.time() - start      
print(x)
print(t)
[[     0. 500000. 500101. ... 498121. 497922. 497740.]
 [     0. 500000. 499894. ... 502050. 502174. 502112.]
 [     0. 500000. 499989. ... 501703. 501836. 502081.]
 ...
 [     0. 500000. 499804. ... 499600. 499526. 499371.]
 [     0. 500000. 499923. ... 500371. 500184. 500247.]
 [     0. 500000. 500007. ... 501172. 501113. 501254.]]
0.06389498710632324

这个小改动应该会让您的工作流程更快(在我的 T4 GPU 上最初是 0.06 秒对 0.6 秒)。请注意,注释中的.get()方法用于在不相等比较之前将sum操作的结果从GPU显式传输到CPU。这不是必需的,因为 CuPy 知道如何处理逻辑操作,但会给您带来非常小的额外加速。

【讨论】:

以上是关于将数组的列作为向量执行“for循环”时,Cupy 比 numpy 慢的主要内容,如果未能解决你的问题,请参考以下文章

For 循环增量器不能作为字符串向量的索引

MATLAB的流程控制

为numpy 3D数组加速卷积循环?

如何使用训练有素的模型预测 Cupy 数组?

如何在for-of中使用array.slice转换for循环或为生成的列和行映射array.slice?

来自 for 循环的分段错误