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

问题描述:

例如,cudaMalloc((void**)&device_array, num_bytes);为什么cudaMalloc()使用指针指针?

这个问题已经asked面前,得到的答复是“因为cudaMalloc返回错误代码”,但我不明白这一点 - 什么有双指针得到了与返回错误做码?为什么不能用简单的指针来完成这项工作?

如果我写

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

错误代码会被放在catch_status,并返回一个简单的指向分配GPU的内存就足够了,不应该吗?

+12

它是一个C API,没有其他的方式来通过引用传递指针。 – talonmies

在C中,数据可以通过值或通过simulated pass-by-reference(即通过指向数据的指针)传递给函数。按价值是一种单向方法,通过指针允许函数与其调用环境之间的双向数据流。

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

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

+1

啊,我明白了。我把它与返回值混在一起,并通过参考传递......谢谢! – rhyc

+0

因此,简单来说,您将指针传递给指向未来数组的指针,因为您希望该方法像通常那样更改_pointer_而不是_array_。 –

添加到罗伯特的答案,但首先重申,它是一个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. 直接传递设备指针,没有呼叫时使用地址运算符(&)并且不会投射到void类型。
  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功能可以设计,因为它遵循也:

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; 
p = (float *) cudaMalloc(sizeof(float), &errorCode); 

这两种方法是相同的功能,而:

float *p; 
int errorCode; 
errorCode = cudaMalloc((void**)&p, sizeof(float)); 

虽然如下所述第二个可用于他们有不同的签名,cuda的人决定采用第一种方法,返回错误代码并通过指针分配内存,而大多数人认为第二种方法会是更好的选择。