Numba cuda:为什么一维数组的总和不对?

Numba cuda: why the sum of the 1D array is not right?

我正在练习 numba 和 cuda 编程。我试图用 cuda 总结一系列的。总和不正确。我认为一定有一些东西可以在最后正确地同步和收集数据。

 @cuda.jit
def my_kernel(const_array, res_array):

    sbuf = cuda.shared.array(512, float32)

    # Thread id in a 1D block
    tx = cuda.threadIdx.x
    # Block id in a 1D grid
    ty = cuda.blockIdx.x
    # Block width, i.e. number of threads per block
    bw = cuda.blockDim.x
    # Compute flattened index inside the array
    pos = tx + ty * bw

    sbuf[tx] = 0

    if pos < const_array.shape[0]:

        sbuf[tx] = const_array[pos] # do the computation

    cuda.syncthreads()
    if cuda.threadIdx.x == 0:
        for i in range(bw):
            res_array[0] += sbuf[i] 


    return


data_size = 10000000
res = numpy.zeros(1, dtype=numpy.float64)
const_array = numpy.ones(data_size, dtype=numpy.int8)

threadsperblock = 512
blockspergrid = math.ceil(data_size / threadsperblock)

my_kernel[blockspergrid, threadsperblock](const_array, res)

print(res)        

每次我 运行 这段代码都会检索不同的值,例如28160.0,当然必须是10m。

并提示?

首先,求和逻辑根本没有意义,而且效率很低。问题是您试图从不同块中的不同线程写入单个内存位置,这会导致竞争条件。您应该使用 cuda.atomic.add 来避免竞争条件。您可以在 CUDA 编程指南中阅读更多内容:https://docs.nvidia.com/cuda/cuda-c-programming-guide/index.html#atomic-functions

问题似乎是您没有对整组块求和。您有一个 10000000 的矢量维度和 512 个线程,这意味着您需要对所有块求和 19532 个块。这是通过启动多个内核(主要用于旧设备)或使用原子操作以标准 CUDA 语言实现的。具体来说,您的问题出在这部分代码中:

if pos < const_array.shape[0]:
    sbuf[tx] = const_array[pos] # do the computation    cuda.syncthreads()
if cuda.threadIdx.x == 0:
    for i in range(bw):
        res_array[0] += sbuf[i] 

在前两行中,您将数据从全局复制到数组sbuf 的共享内存中。但是,不同块中的所有线程同时尝试将它们的本地数据添加到位于 res_array 的全局内存地址中,这不是顺序的,不同的线程可能只是读取相同的数据两次并给你错误的结果。解决方法是先在共享内存中进行部分求和,再进行原子求和,避免异步读写操作

if cuda.threadIdx.x == 0:
    sum = 0
    for i in range(bw):
        sum  += sbuf[i] 
    cuda.atomic.add(res_array, 0, sum)

这应该可以解决您的问题。

此致。