CUDA \ NUMBA同步速度较慢

时间:2018-03-08 01:16:27

标签: python cuda numba

我是CUDA的新手,我正试图用NUMBA \ CUDA来加速我的代码。 但是,由于我的代码非常慢,我遇到了一些麻烦。示例代码如下所示。

from timeit import default_timer as timer
from numba import jit, guvectorize, int32, int64, float64
from numba import cuda

@cuda.jit
def f_vec_loops(x, ret, maxiter):
    nx = len(ret)
    ny = len(ret[0])
    for k in range(maxiter):
        for i in range(nx):
            for j in range(ny):
                ret[i, j] += x[i, j]


x = 1024
y = 1024
a = np.ones([x, y], dtype='int32')
ret = np.zeros([x, y], dtype='int32')

a_cuda = cuda.to_device(a)
ret_cuda = cuda.to_device(ret)
maxiter = 100

s = timer()
cuda.synchronize()
f_vec_loops(a_cuda, ret_cuda, maxiter)
cuda.synchronize()

print(timer() - s)

s = timer()
trt = ret_cuda.copy_to_host()
print(trt)
print(timer()-s)

is代码的输出是后续的:

24.132136431649194
[[100 100 100 ..., 100 100 100]
 [100 100 100 ..., 100 100 100]
 [100 100 100 ..., 100 100 100]
 ...,
 [100 100 100 ..., 100 100 100]
 [100 100 100 ..., 100 100 100]
 [100 100 100 ..., 100 100 100]]
 0.03437229066477343

正如您所看到的,与问题的复杂性相关的结果时间是巨大的。我试图隔离单个GPU功能,结果时间非常短:

0.1956893293540045

我实现了取消" cuda.synchronize()",因此在某种程度上必须连接到线程的同步。 但是,我不知道如何解决这个问题。 欢迎任何帮助!

1 个答案:

答案 0 :(得分:2)

这里有两个基本错误 - 你的内核是完全串行的,你正在运行一个线程。您通过删除cuda.synchronize()看到的明显加速只是改变您测量的结果。内核启动API是异步的,因此删除同步调用只是意味着您只测量内核启动时间,而不是总内核执行时间。

您的内核可以通过以下方式进行简单修改:

from timeit import default_timer as timer
from numba import jit, guvectorize, int32, int64, float64
from numba import cuda
import numpy as np
import math

@cuda.jit
def f_vec_loops2(x, ret, maxiter):
    nx = len(ret)
    ny = len(ret[0])

    i, j = cuda.grid(2)
    if (i < nx) & (j < ny):
        value = 0
        for k in range(maxiter):
            value += x[i, j]

        ret[i, j] = value

@cuda.jit
def f_vec_loops(x, ret, maxiter):
    nx = len(ret)
    ny = len(ret[0])
    for k in range(maxiter):
        for i in range(nx):
            for j in range(ny):
                ret[i, j] += x[i, j]


x = 1024
y = 1024
a = np.ones([x, y], dtype='int32')
ret = np.zeros([x, y], dtype='int32')

a_cuda = cuda.to_device(a)
ret_cuda = cuda.to_device(ret)
maxiter = 100

s = timer()
f_vec_loops(a_cuda, ret_cuda, maxiter)
cuda.synchronize()
print(timer() - s)

threadsperblock = (16, 16)
blockspergrid_x = math.ceil(x / threadsperblock[0])
blockspergrid_y = math.ceil(y / threadsperblock[1])
blockspergrid = (blockspergrid_x, blockspergrid_y)

s = timer()
f_vec_loops2[blockspergrid, threadsperblock](a_cuda, ret_cuda, maxiter)
cuda.synchronize()
print(timer() - s)

s = timer()
trt = ret_cuda.copy_to_host()
print(trt)
print(timer()-s)

跑步时,你会看到类似的东西:

In [2]: %run something.py
24.983618166297674
0.11915503349155188
[[100 100 100 ..., 100 100 100]
 [100 100 100 ..., 100 100 100]
 [100 100 100 ..., 100 100 100]
 ..., 
 [100 100 100 ..., 100 100 100]
 [100 100 100 ..., 100 100 100]
 [100 100 100 ..., 100 100 100]]
0.002271135337650776

与原始(完全串行)代码相比,它提供了大约200倍的加速。

在CUDA中编写并行代码的基本概念在书籍,教程,博客,Stack Overflow问题以及工具包文档本身中有详细描述。 Numba Python CUDA语言非常忠实地复制了基本CUDA C语言的子集,从CUDA C学习CUDA Python的障碍非常低。您需要做的就是阅读。