使用cudamalloc()。为什么双指针?

时间:2011-11-03 00:37:24

标签: c cuda malloc

我目前正在浏览http://code.google.com/p/stanford-cs193g-sp2010/上的教程示例以学习CUDA。下面给出了演示__global__函数的代码。它只创建了两个阵列,一个在CPU上,一个在GPU上,用7号填充GPU阵列,并将GPU阵列数据复制到CPU阵列中。

#include <stdlib.h>
#include <stdio.h>

__global__ void kernel(int *array)
{
  int index = blockIdx.x * blockDim.x + threadIdx.x;

  array[index] = 7;
}

int main(void)
{
  int num_elements = 256;

  int num_bytes = num_elements * sizeof(int);

  // pointers to host & device arrays
  int *device_array = 0;
  int *host_array = 0;

  // malloc a host array
  host_array = (int*)malloc(num_bytes);

  // cudaMalloc a device array
  cudaMalloc((void**)&device_array, num_bytes);

  int block_size = 128;
  int grid_size = num_elements / block_size;

  kernel<<<grid_size,block_size>>>(device_array);

  // download and inspect the result on the host:
  cudaMemcpy(host_array, device_array, num_bytes, cudaMemcpyDeviceToHost);

  // print out the result element by element
  for(int i=0; i < num_elements; ++i)
  {
    printf("%d ", host_array[i]);
  }

  // deallocate memory
  free(host_array);
  cudaFree(device_array);
} 

我的问题是为什么他们用双指针措辞cudaMalloc((void**)&device_array, num_bytes);语句?即使是here cudamalloc()on的定义,第一个参数也是双指针。

为什么不简单地在GPU上返回指向已分配内存开头的指针,就像CPU上的malloc函数一样?

5 个答案:

答案 0 :(得分:20)

所有CUDA API函数都会返回错误代码(如果没有错误,则返回cudaSuccess)。所有其他参数都通过引用传递。但是,在普通C中,您不能有引用,这就是为什么您必须传递要存储返回信息的变量的地址。由于您要返回一个指针,因此需要传递一个双指针。

另一个众所周知的函数是scanf函数,它在地址上运行的原因是&。有多少次您忘记在要将值存储到的变量之前写入int i; scanf("%d",&i); ? ;)

{{1}}

答案 1 :(得分:18)

这只是一个可怕的,可怕的API设计。为获得抽象(void *)内存的分配函数传递双指针的问题是你必须创建一个void *类型的临时变量来保存结果,然后将它分配给真正的指针您要使用的正确类型。与(void**)&device_array一样,转换是无效的C并导致未定义的行为。你应该简单地编写一个与普通malloc一样的包装函数并返回一个指针,如:

void *fixed_cudaMalloc(size_t len)
{
    void *p;
    if (cudaMalloc(&p, len) == success_code) return p;
    return 0;
}

答案 2 :(得分:7)

我们将它转​​换为双指针,因为它是指向指针的指针。它必须指向GPU内存的指针。 cudaMalloc()所做的是它在GPU上分配一个内存指针(带空格),然后由我们给出的第一个参数指向。

答案 3 :(得分:2)

在C / C ++中,您可以通过调用malloc函数在运行时动态分配内存块。

int * h_array
h_array = malloc(sizeof(int))

malloc函数返回分配的内存块的地址,该内存块可以存储在某种指针的变量中。
CUDA中的内存分配在两个方面略有不同,

  1. cudamalloc返回一个整数作为错误代码而不是a 指向内存块的指针。
  2. 除了字节大小外 已分配,cudamalloc还需要一个双空指针作为其 第一个参数。

    int * d_array cudamalloc((void **)&amp; d_array,sizeof(int))

  3. 第一个区别背后的原因是所有CUDA API函数都遵循返回整数错误代码的惯例。因此,为了使事情保持一致,cudamalloc API也会返回一个整数。

    需要双指针,因为函数的第一个参数可以分两步理解。

    首先,由于我们已经决定使cudamalloc返回一个整数值,我们不能再使用它来返回已分配内存的地址。在C中,函数通信的唯一方法是将指针或地址传递给函数。该函数可以更改存储在地址或指针指向的地址的值。稍后可以使用相同的内存地址在函数范围外检索对这些值的更改。

    双指针如何工作

    下图说明了它如何与双指针一起使用。

    int cudamalloc((void **) &d_array, int type_size) {
      *d_array = malloc(type_size)
      return return_code
    }
    

    enter image description here

    为什么我们需要双指针?为什么这样做

    我通常生活在蟒蛇世界,所以我也很难理解为什么这不起作用。

    int cudamalloc((void *) d_array, int type_size) {
      d_array = malloc(type_size)
      ...
      return error_status
    }
    

    那为什么它不起作用?因为在C中,当调用cudamalloc时,会创建一个名为d_array的局部变量,并使用第一个函数参数的值进行赋值。我们无法在函数范围之外检索该局部变量中的值。这就是我们需要指向指针的原因。

    int cudamalloc((void *) d_array, int type_size) {
      *d_array = malloc(type_size)
      ...
      return return_code
    }
    

    enter image description here

答案 4 :(得分:1)

问题:您必须返回两个值:返回代码和指向内存的指针(如果返回代码表示成功)。所以你必须使其中一个指向返回类型。作为返回类型,您可以选择返回指向int的指针(用于错误代码)或返回指向指针的指针(用于存储器地址)。有一个解决方案和另一个解决方案一样好(其中一个产生指向指针的指针(我更喜欢使用这个术语而不是双指针,因为这听起来更像是指向双浮点的指针数))。

在malloc中你有一个很好的属性,你可以有空指针来指示一个错误,所以你基本上只需要一个返回值..我不确定这是否可能与指向设备内存的指针,因为它可能是没有或错误的空值(记住:这是CUDA和 Ansi C)。可能是主机系统上的空指针与用于设备的null完全不同,因此返回空指针以指示错误不起作用,并且您必须以这种方式创建API(这也意味着你在两个设备上都没有常见的NULL。)