我目前正在浏览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
函数一样?
答案 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中的内存分配在两个方面略有不同,
cudamalloc
返回一个整数作为错误代码而不是a
指向内存块的指针。 除了字节大小外
已分配,cudamalloc
还需要一个双空指针作为其
第一个参数。
int * d_array cudamalloc((void **)&amp; d_array,sizeof(int))
第一个区别背后的原因是所有CUDA API函数都遵循返回整数错误代码的惯例。因此,为了使事情保持一致,cudamalloc
API也会返回一个整数。
需要双指针,因为函数的第一个参数可以分两步理解。
首先,由于我们已经决定使cudamalloc返回一个整数值,我们不能再使用它来返回已分配内存的地址。在C中,函数通信的唯一方法是将指针或地址传递给函数。该函数可以更改存储在地址或指针指向的地址的值。稍后可以使用相同的内存地址在函数范围外检索对这些值的更改。
下图说明了它如何与双指针一起使用。
int cudamalloc((void **) &d_array, int type_size) {
*d_array = malloc(type_size)
return return_code
}
我通常生活在蟒蛇世界,所以我也很难理解为什么这不起作用。
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
}
答案 4 :(得分:1)
问题:您必须返回两个值:返回代码和指向内存的指针(如果返回代码表示成功)。所以你必须使其中一个指向返回类型。作为返回类型,您可以选择返回指向int的指针(用于错误代码)或返回指向指针的指针(用于存储器地址)。有一个解决方案和另一个解决方案一样好(其中一个产生指向指针的指针(我更喜欢使用这个术语而不是双指针,因为这听起来更像是指向双浮点的指针数))。
在malloc中你有一个很好的属性,你可以有空指针来指示一个错误,所以你基本上只需要一个返回值..我不确定这是否可能与指向设备内存的指针,因为它可能是没有或错误的空值(记住:这是CUDA和不 Ansi C)。可能是主机系统上的空指针与用于设备的null完全不同,因此返回空指针以指示错误不起作用,并且您必须以这种方式创建API(这也意味着你在两个设备上都没有常见的NULL。)