在CUDA中初始化设备数组

6

如何初始化使用cudaMalloc()分配的设备数组?

我尝试使用cudaMemset,但它无法初始化除0.code之外的所有值。下面是cudaMemset的示例,其中值初始化为5。

cudaMemset(devPtr,value,number_bytes)

1
你能提供一下你调用 cudaMemset 的代码吗? - geek
3
你知道在 cudaMemset 函数中,参数的值是 字节 值,而不是单词值,也就是和C标准库中的 memset 函数一样吗? - talonmies
2个回答

13

正如您所发现的那样,cudaMemset 的工作方式类似于 C 标准库中的 memset。引用文档:

cudaError_t cudaMemset  (   void *      devPtr,
                            int         value,
                            size_t      count    
                        )           

使用值value填充由devPtr指向的内存区域的前count个字节。

因此,value是一个字节值。如果您执行以下操作:

int *devPtr;
cudaMalloc((void **)&devPtr,number_bytes);
const int value = 5;
cudaMemset(devPtr,value,number_bytes);

您需要的是将 devPtr 的每个 字节 设置为 5。如果 devPtr 是一个整数数组,则结果将是每个整数单词都具有值 84215045。这可能不是您想要的。

使用运行时 API,您可以编写自己的通用内核来执行此操作。它可能非常简单,例如:

template<typename T>
__global__ void initKernel(T * devPtr, const T val, const size_t nwords)
{
    int tidx = threadIdx.x + blockDim.x * blockIdx.x;
    int stride = blockDim.x * gridDim.x;

    for(; tidx < nwords; tidx += stride)
        devPtr[tidx] = val;
}

(标准免责声明:在浏览器中编写,从未编译,从未测试,使用风险自负)。

只需为所需类型实例化模板,并使用适当的网格和块大小调用它,注意现在最后一个参数是单词计数,而不是像cudaMemset中的字节计数。这与cudaMemset的操作并没有什么不同,使用该API调用会导致内核启动,这与我上面发布的内容并没有太大区别。

或者,如果您可以使用驱动程序API,则有cuMemsetD16cuMemsetD32,它们执行相同的操作,但针对半个和全32位单词类型。如果您需要设置64位或更大的类型(因此是双精度或矢量类型),则最好使用自己的内核。


目前,在我的代码中,我正在做同样的事情,但我想用只有cudaMemset的方法。 - username_4567
1
@user997704:使用cudaMemset没有办法。要么运行自己的内核,要么使用驱动程序API中的cuMemsetD32/cuMemsetD32 - talonmies
如果能够自我包含,那么调用这个内核的合理网格和块大小会很好。例如,如果我的数组是N=2333,我该如何调用这个内核? - SkyWalker
我其实并不太理解步幅循环。为什么需要循环呢?每个线程不应该只设置一个元素然后结束吗? - SkyWalker
1
@GiovanniAzua 我认为步幅存在是为了让一个线程初始化数组中的多个元素。有时候,将更多的工作交给一个线程比启动很多线程更好。 - BRabbit27

1
我也需要解决这个问题,但是我真的不理解其他提出的解决方案。 特别是我不明白为什么它要迭代网格块 for(; tidx < nwords; tidx += stride) ,以及内核调用和为什么使用反直觉的单词大小。
因此,我创建了一个更简单的单片通用内核,并使用步幅进行了自定义。 例如,您可以使用它以多种方式初始化矩阵,例如将行或列设置为任何值:
template <typename T>
__global__ void kernelInitializeArray(T* __restrict__ a, const T value, 
   const size_t n, const size_t incx) {
      int tid = threadIdx.x + blockDim.x * blockIdx.x;
      if (tid*incx < n) {
           a[tid*incx] = value;
       }
}

然后你可以像这样调用内核:

template <typename T>
void deviceInitializeArray(T* a, const T value, const size_t n, const size_t incx) {
      int number_of_blocks = ((n / incx) + BLOCK_SIZE - 1) / BLOCK_SIZE;
      dim3 gridDim(number_of_blocks, 1);
      dim3 blockDim(BLOCK_SIZE, 1);
      kernelInitializeArray<T> <<<gridDim, blockDim>>>(a, value, n, incx);
}

2
如果你想了解为什么循环是一个好主意,可以看看这个答案 - talonmies

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接