【问题标题】:Why does cudaMalloc() use pointer to pointer?为什么 cudaMalloc() 使用指向指针的指针?
【发布时间】:2012-10-07 20:50:36
【问题描述】:

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

这个问题之前是asked,回复是“因为cudaMalloc返回错误代码”,但我不明白——双指针与返回错误代码有什么关系?为什么一个简单的指针不能完成这项工作?

如果我写

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

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

【问题讨论】:

  • 它是一个C API,没有其他方法可以通过引用传递指针。

标签: c++ c pointers cuda


【解决方案1】:

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

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

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

【讨论】:

  • 啊,我明白了。我混淆了返回值并通过引用传递...谢谢!
  • 所以简单来说,您将指针 a 传递给指向未来数组的指针,因为您希望方法更改该 pointer 而不是 array 像往常一样。
【解决方案2】:

添加到罗伯特的答案,但首先重申,它是一个 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. 直接传递设备指针,调用时不使用地址运算符 (&amp;),也不强制转换为 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;

【讨论】:

    【解决方案3】:

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

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

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

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

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

    而第二个可以如下使用:

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

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

    【讨论】:

      猜你喜欢
      • 2011-12-20
      • 2011-12-16
      • 1970-01-01
      • 2021-07-07
      • 2021-12-04
      • 2018-02-09
      • 1970-01-01
      • 2012-07-23
      • 2017-08-26
      相关资源
      最近更新 更多