CUDA真的没有类似于calloc()的API调用吗?

6

从查看CUDA 5.5 API参考CUDA C程序员指南得知,似乎没有cudaCalloc(),这是标准C库的calloc()在GPU上的等效函数。

  • 真的没有API功能可以分配一个以全零初始化的缓冲区吗?
  • 有没有比调用cudaMalloc()cudaMemset()更好的方法?

6
一个编译器库很可能会将calloc实现为malloc和memset的包装器。 - Lundin
3
@Lundin:我认为可能不行。你可以分配零内存块而不实际设置任何数据,只需标记块为空(例如未从虚拟内存映射到物理内存,并且写入它们会导致页面错误)。当然,这取决于你所在的机器。我不确定GPU上的内存清零是如何发生的,但很可能你可以比malloc+memset做得更好。 - einpoklum
1
使用 thrust::device_vector - Jared Hoberock
@JaredHoberock:你能把它作为答案并解释一下在这种情况下使用thrust::device_vector是一个好主意的原因吗? - einpoklum
1
在下面的答案中,有没有什么满意的?如果有,请接受它。你有一个不好的习惯,就是抛石头藏手。 - Vitality
@JackOLantern:我并不太喜欢RobertCrovella的回答中的宏,但基本上答案是否定的,这些是解决方法的API调用... - einpoklum
4个回答

13

是否真的没有API功能可以分配一个初始化为全零的缓冲区?

确实没有。

有没有更好的方法,而不是使用cudaMalloc()后跟cudaMemset()?

如果只是为了方便(如果第一个问题的答案是否定的,您没有告诉我们更好的意思),您可以使用宏:

#define cudaCalloc(A, B, C) \
    do { \
        cudaError_t __cudaCalloc_err = cudaMalloc(A, B*C); \
        if (__cudaCalloc_err == cudaSuccess) cudaMemset(*A, 0, B*C); \
    } while (0)

上述宏将与我通常使用的错误检查一起工作(基于使用 cudaGetLastError() );或者,如果您愿意,您可以直接将您喜欢的错误检查构建到宏中。有关错误处理,请参见 此问题


1
重新审视你的回答 - 为什么要使用宏而不是标记为“inline”的函数? - einpoklum
你可以选择这样做。 - Robert Crovella
1
我在想,在我们这个更文明的时代(至少对于编程来说是这样),最好编辑你的答案,推荐使用其他方法。在我看来,除非绝对必要,否则不应该鼓励使用宏。顺便说一下,Nikolay Sakharnykh问候你。或者说,我提到了你的名字,他表示了认可 :-) - einpoklum
为什么不添加一个答案呢?这样你就可以得到更文明的积分。我会给你点赞的。你甚至可以取消接受这个答案,然后接受你自己的答案。 - Robert Crovella

1

以下是一个内联函数的解决方案。devPtr 应该是指向任何东西的指针。将 void* 作为函数参数可以使调用者免除进行 类型转换 的烦恼。

inline cudaError_t
_cuda_calloc( void *devPtr, size_t size )
{
  cudaError_t err = cudaMalloc( (void**)devPtr, size );
  if( err == cudaSuccess ) err = cudaMemset( *(void**)devPtr, 0, size );
  return err;
}

已经建议。 - einpoklum
建议但并非明确规定。所有与CUDA相关的SO帖子仍然编写宏,而不是内联函数。 除非需要C兼容性,否则没有理由保留这种危险的习惯的示例。 - Dimitri Lesnoff
我的编辑已经被批准,但立即被回滚了,而且没有修复这个答案中的问题。 - Dimitri Lesnoff
我的编辑已被批准,但立即进行了回滚,未修复此答案中的问题。 事实并非我们使用void*就可以使调用者不必应用转换,而仅仅是因为在函数调用期间,void及其任何间接级别的空指针函数参数都可以接受任何其他类型而无需转换。 snake_case被使用,而所有其他CUDA函数均使用camelCase。 “应该”是一个相当不精确的表述。 它受到了另一个答案的很大启示,但没有给予任何功劳。而且devPtr不是指向另一个指针的指针。 - Dimitri Lesnoff
cudaMalloc 修改了它的第一个参数,因此需要一个指向指针变量的指针。我们想要像 _cuda_calloc(&p,...) 这样调用函数,其中 p 可以是指向 int、float 或任何其他类型的指针。如果 devPtr 声明为 void**,则调用该函数将需要进行转换。请参见答案中的链接。"应该是"有什么不准确之处? - Claas Bontus

1
如果你只想简单地将新分配的内存清零,可以使用 thrust::device_vector,它会默认构造其元素。对于原始类型来说,这与 calloc 的行为相同。

推力函数仅应在计划整个程序中始终使用推力向量时使用。与原始向量的可能性相比,推力库功能今天仍然有限。 - Dimitri Lesnoff

0

CUDA Runtime API中没有类似于calloc()的功能,也没有其他低级别的等效功能。相反,您可以执行以下操作:

cudaMalloc(&ptr, size);
cudaMemset(ptr, 0, size);

请注意,这全部都是同步的。还有一个 cudaMemsetAsync() 函数,但实话说,cudaMalloc() 目前的速度已经足够慢了,所以并不重要。

2
使用Memcpy来清零缓冲区是一个相当糟糕的想法,我认为。 - einpoklum
cudaMemset() 无论如何都是异步运行的(请参见参考手册)。 - Tom
1
并非完全不对,该函数在大多数情况下表现出同步行为。 - TripleS

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