例如,cudaMalloc((void**)&device_array, num_bytes);
这个问题以前是asked,回复是"因为cudaMalloc
会返回错误代码"但是我没有得到它 - 什么有双指针与返回错误代码有关吗?为什么一个简单的指针不能完成这项工作?
如果我写
cudaError_t catch_status;
catch_status = cudaMalloc((void**)&device_array, num_bytes);
错误代码将被放入catch_status
,并且返回一个指向分配的GPU内存的简单指针就足够了,不应该吗?
答案 0 :(得分:65)
在C中,数据可以通过值或通过simulated pass-by-reference(即通过指向数据的指针)传递给函数。值是一种单向方法,通过指针允许函数与其调用环境之间的双向数据流。
当数据项通过函数参数列表传递给函数时,该函数应该修改原始数据项,以便修改后的值出现在调用环境中,正确的C方法是通过指针的数据项。在C中,当我们通过指针时,我们获取要修改的项的地址,创建一个指针(在这种情况下可能是指向指针的指针)并将地址交给函数。这允许函数在调用环境中修改原始项(通过指针)。
通常malloc
返回一个指针,我们可以在调用环境中使用赋值将此返回值赋给所需的指针。在cudaMalloc
的情况下,CUDA设计者选择使用返回的值来携带错误状态而不是指针。因此,调用环境中指针的设置必须通过参考(即通过指针)传递给函数的参数之一来进行。由于它是我们想要设置的指针值,我们必须获取指针的地址(创建指针指针)并将该地址传递给cudaMalloc
函数。
答案 1 :(得分:9)
添加罗伯特的答案,但首先重申,它是一个C API,这意味着它不支持引用,这将允许您修改指针的值(不仅仅是什么是指向)函数内部。 Robert Crovella的回答解释了这一点。另请注意,它必须是void
,因为C也不支持函数重载。
此外,在C ++程序中使用C API时(但您没有说明这一点),通常将此类函数包装在模板中。例如,
template<typename T>
cudaError_t cudaAlloc(T*& d_p, size_t elements)
{
return cudaMalloc((void**)&d_p, elements * sizeof(T));
}
与调用上述cudaAlloc
函数的方式有两点不同:
&
),也不会转换为void
类型。elements
现在是元素的数量而不是字节数。 sizeof
运算符有助于实现此目的。这可以说是更直观的指定元素而不用担心字节。例如:
float *d = nullptr; // floats, 4 bytes per elements
size_t N = 100; // 100 elements
cudaError_t err = cudaAlloc(d,N); // modifies d, input is not bytes
if (err != cudaSuccess)
std::cerr << "Unable to allocate device memory" << std::endl;
答案 2 :(得分:2)
我想通过一个例子可以更好地解释cudaMalloc
函数的签名。它基本上通过指向该缓冲区的指针(指向指针的指针)分配缓冲区,类似以下方法:
int cudaMalloc(void **memory, size_t size)
{
int errorCode = 0;
*memory = new char[size];
return errorCode;
}
如您所见,该方法采用指向指针的memory
指针,在指针上保存新分配的内存。然后它返回错误代码(在这种情况下是一个整数,但它实际上是一个枚举)。
cudaMalloc
函数的设计也可以如下:
void * cudaMalloc(size_t size, int * errorCode = nullptr)
{
if(errorCode)
errorCode = 0;
char *memory = new char[size];
return memory;
}
在第二种情况下,错误代码通过隐式设置为null的指针设置(对于人们根本不打扰错误代码的情况)。然后返回分配的内存。
第一种方法可以和现在的实际cudaMalloc
一样使用:
float *p;
int errorCode;
errorCode = cudaMalloc((void**)&p, sizeof(float));
虽然第二个可以使用如下:
float *p;
int errorCode;
p = (float *) cudaMalloc(sizeof(float), &errorCode);
这两种方法在功能上是等价的,虽然它们有不同的签名,cuda的人决定选择第一种方法,返回错误代码并通过指针分配内存,而大多数人说第二种方法会是一个更好的选择。