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

smi*_*dha 41 c malloc cuda

我目前正在浏览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);
} 
Run Code Online (Sandbox Code Playgroud)

我的问题是为什么他们cudaMalloc((void**)&device_array, num_bytes);用双指针措辞?即使在这里定义cudamalloc()on说第一个参数是双指针.

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

Cyg*_*sX1 22

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

由于相同原因在地址上操作的另一个众所周知的功能是scanf功能.有多少次你忘记&在要存储值的变量之前写这个?;)

int i;
scanf("%d",&i);
Run Code Online (Sandbox Code Playgroud)

  • 这是必需的,因为函数*设置*指针.与C中的每个输出参数一样,您需要一个指向您设置的实际变量的指针,而不是值本身. (3认同)

R..*_*R.. 20

这只是一个可怕的,可怕的API设计.为获取abstract(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;
}
Run Code Online (Sandbox Code Playgroud)

  • 要编写一个短语,那将是"简单的一个可怕的,可怕的API设计".最好在各种API中获得一致的返回值,并传回分配的指针; 然后你又回到了我们开始的地方. (4认同)
  • 你会如何返回错误代码和指针呢?请注意,错误处理应留给API的用户,因此_has_将被返回. (4认同)
  • 我相信CUDART有一个用于cudaMalloc()的模板化包装器,它使(void**)转换成为不必要的.此外,这里给出的功能不是我建议投入生产的东西; 它隐藏了cudaMalloc()返回值提供的过多有用信息. (2认同)
  • 同意,对于这个函数,它可以像这样工作,但不是对于所有函数,NULL将是一个错误.我强烈反对你关于"愚蠢的一致性"的陈述.从长远来看,缺乏一致性会导致混乱.编译器错误消息可以帮助您捕获即时错误,但是首先点击这些错误会减慢开发过程,并且无法帮助您阅读已有的代码!人类是有限的,并要求他们记住不一致的API将无法正常工作.因为那,我对你"小心"吗?我觉得有点侮辱. (2认同)

jwd*_*msd 8

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

  • 当cudaMalloc的第一个参数需要**时,你的答案用简单的语言解释.使用你的答案,我能够理解双指针API设计背后的逻辑.第一个取消引用现在将指向一个实际指向存储在设备存储器中的数据的指针.第二个解除引用实际上会指向感兴趣的矢量 (3认同)

小智 6

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

int * h_array;
h_array = malloc(sizeof(int));
Run Code Online (Sandbox Code Playgroud)

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

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

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

第一个区别的原因是所有CUDA API函数都遵循返回整数错误代码的约定。因此,为了使事情保持一致,cudamallocAPI还返回一个整数。

可以通过两个步骤来理解将双指针作为函数的第一个参数的要求。

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

双指针如何工作

下图说明了如何使用双指针。

int cudamalloc((void **) &d_array, int type_size) {
  *d_array = malloc(type_size);
  return return_code;
}
Run Code Online (Sandbox Code Playgroud)

在此处输入图片说明

为什么需要双指针?为什么这样做有效

我通常生活在python世界中,所以我也很难理解为什么这行不通。

int cudamalloc((void *) d_array, int type_size) {
  d_array = malloc(type_size);
  ...
  return error_status;
}
Run Code Online (Sandbox Code Playgroud)

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

int cudamalloc((void *) d_array, int type_size) {
  *d_array = malloc(type_size);
  ...
  return return_code;
}
Run Code Online (Sandbox Code Playgroud)

在此处输入图片说明