为什么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
函数有两个区别:
- 直接传递设备指针,调用时不需要使用地址运算符(
&
),也不需要转换为void
types。 - 第二个参数
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。