为什么cudaMalloc()使用指针指针?

例如, cudaMalloc((void**)&device_array, num_bytes);

这个问题之前已经被问到了 ,答复是“因为cudaMalloc返回一个错误代码”,但我没有得到它 – 什么有一个双指针与返回错误代码? 为什么不能用简单的指针来完成这个工作?

如果我写

 cudaError_t catch_status; catch_status = cudaMalloc((void**)&device_array, num_bytes); 

错误代码将被放入catch_status ,并返回一个简单的指针分配的GPU内存应该就够了,不是吗?

在C中,数据可以通过值或通过模拟传递引用 (即通过指向数据的指针)传递给函数。 价值是一种单向的方法,通过指针允许函数和其调用环境之间的双向数据stream。

当一个数据项通过函数参数列表被传递给一个函数,并且该函数需要修改原始数据项,以使修改后的值在调用环境中出现时,正确的C方法是通过数据项通过指针。 在C中,当我们通过指针传递的时候,我们把要修改的项目的地址,创build一个指针(在这种情况下可能是一个指针指针),并将地址交给函数。 这允许该function在呼叫环境中修改原始项目(通过指针)。

通常malloc返回一个指针,我们可以在调用环境中使用赋值将这个返回的值赋给所需的指针。 在cudaMalloc的情况下,CUDAdevise人员select使用返回值来携带错误状态而不是指针。 因此,调用环境中指针的设置必须通过传递给函数的参数之一,通过引用(即通过指针)来实现。 由于它是我们想要设置的指针值 ,我们必须获取指针的地址(创build一个指向指针的指针)并将该地址传递给cudaMalloc函数。

添加到罗伯特的答案,但首先重申,它是一个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函数有两个区别:

  1. 直接传递设备指针,调用时不需要使用地址运算符( & ),也不需要转换为voidtypes。
  2. 第二个参数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; 

我想通过一个例子可以更好地解释cudaMalloc函数的签名。 基本上是通过指向该缓冲区的指针(指向指针的指针)分配缓冲区, 下面的方法:

 int cudaMalloc(void **memory, size_t size) { int errorCode = 0; *memory = new char[size]; return errorCode; } 

正如你所看到的,该方法需要一个指向memory指针,在内存中保存新分配的内存。 然后它返回错误代码(在这种情况下是一个整数,但它实际上是一个枚举)。

cudaMalloc函数也可以这样devise:

 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); 

这两个方法在function上是等价的,但它们具有不同的签名,而cuda的人决定去第一个方法,返回错误代码并通过指针分配内存,而大多数人则说第二个方法是更好的select。