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

rhy*_*hyc 43 c c++ pointers cuda

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

之前已经过这个问题,回复是"因为cudaMalloc返回错误代码",但是我没有得到它 - 什么有双指针与返回错误代码有什么关系?为什么一个简单的指针不能完成这项工作?

如果我写

cudaError_t catch_status;
catch_status = cudaMalloc((void**)&device_array, num_bytes);
Run Code Online (Sandbox Code Playgroud)

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

Rob*_*lla 68

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

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

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


cha*_*pjc 9

添加到Robert的答案,但首先重申,它是一个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));
}
Run Code Online (Sandbox Code Playgroud)

如何调用上述cudaAlloc函数有两点不同:

  1. 直接传递设备指针,&在调用时不使用address-of运算符(),也不需要转换为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;
Run Code Online (Sandbox Code Playgroud)


meJ*_*rew 5

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

int cudaMalloc(void **memory, size_t size)
{
    int errorCode = 0;

    *memory = new char[size];

    return errorCode;
}
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,该方法采用一个memory指向指针的指针,在该指针上保存新分配的内存。然后它返回错误代码(在本例中为整数,但它实际上是一个枚举)。

cudaMalloc函数也可以设计如下:

void * cudaMalloc(size_t size, int * errorCode = nullptr)
{
    if(errorCode)
        errorCode = 0;

    char *memory = new char[size];

    return memory;
}
Run Code Online (Sandbox Code Playgroud)

在第二种情况下,错误代码是通过隐式设置为 null 的指针设置的(对于人们根本不关心错误代码的情况)。然后返回分配的内存。

cudaMalloc现在可以直接使用第一种方法:

float *p;
int errorCode;
errorCode = cudaMalloc((void**)&p, sizeof(float));
Run Code Online (Sandbox Code Playgroud)

而第二个可以使用如下:

float *p;
int errorCode;
p = (float *) cudaMalloc(sizeof(float), &errorCode);
Run Code Online (Sandbox Code Playgroud)

这两种方法在功能上是等价的,虽然它们有不同的签名,cuda 的人决定采用第一种方法,返回错误代码并通过指针分配内存,而大多数人说第二种方法是更好的选择。