CUDA GPU编程指南:内存的申请,释放与拷贝
前言
在CPU上运行的程序,会将数据存放在内存中,在C++中直接使用new delete 或者malloc free就可以管理内存的申请和释放。
在GPU上运行的程序,会将数据放入显存中,需要CUDA提供的API来管理内存,并且在显存中存放的数据,是不能被CPU直接访问的。
主机内存(host)和设备内存(device)
主机内存指在CPU上运行的代码所使用的内存,正常C/C++代码中的使用的变量,无论是在堆还是栈上,使用的都是主机内存。
使用设备内存指在GPU上运行的代码可以使用到的内存。
需要注意的是,核函数和device函数中是不能访问和读取主机内存的。这意味着,在调用核函数时,将数据(主机内存)指针当作参数传入核函数,在核函数中是无法读取到数据的,因为参数的指针指向的是主机内存的地址,而在核函数中读写会被当做设备内存来解析。
因此需要掌握在CPU代码中如何管理设备内存的方法。
在CPU代码中管理设备内存
__host__ cudaError_t cudaMalloc(void **devPtr, size_t size); __host__ cudaError_t cudaFree(void *devPtr);
CUDA提供cudaMalloc 和 cudaFree 来管理设备内存的申请和释放,用法与C语言中的malloc 和 free相似,只是传递数据的方式不同。
使用时需要注意cudaMalloc 的第一个参数是一个二级指针,所以传参的时候一定要注意。
int *arry = 0; int arrySize = 100; //申请一个长度为100的int数组 cudaMalloc((void**)&arry, size * sizeof(int)); //释放内存 cudaFree(arry);
如上述代码,完成了一个int数组的内存的申请与释放。
数据拷贝
上文提到,在核函数中无法读写主机内存中的数据,那么同理,在CPU上运行的代码,也无法读写在设备内存中的数据。
但是在实际应用场景中,一定会需要到两边数据的交互,那么怎么实现?
CUDA提供了数据拷贝函数,以供CPU操作设备内存的数据。
__host__ cudaError_t cudaMemcpy(void *dst, const void *src, size_t count, enum cudaMemcpyKind kind);
dst: 要拷贝到的目标地址
src: 要拷贝的源地址
count:要拷贝的长度,单位是字节。
kind:拷贝数据的方式。
cudaMencpykind的定义是:
enum __device_builtin__ cudaMemcpyKind { cudaMemcpyHostToHost = 0, /**< Host -> Host */ cudaMemcpyHostToDevice = 1, /**< Host -> Device */ cudaMemcpyDeviceToHost = 2, /**< Device -> Host */ cudaMemcpyDeviceToDevice = 3, /**< Device -> Device */ cudaMemcpyDefault = 4 /**< Direction of the transfer is inferred from the pointer values. Requires unified virtual addressing */ };
cudaMemcpyHostToHost :主机 拷贝到 主机
cudaMemcpyHostToDevice :主机拷贝到设备
cudaMemcpyDeviceToHost :设备拷贝到主机
cudaMemcpyDeviceToDevice :设备拷贝到设备
cudaMemcpyDefault :拷贝方式通过指针的地址来推断。
综上所述,cudaMemcpy提供了多种数据拷贝的方式。
使用时需要注意的一点是,在调用数据拷贝函数的时候,CUDA会自动等待同步,即等到GPU中的任务执行完成之后才会真正的开始拷贝数据。
#include "cuda_runtime.h" #include "device_launch_parameters.h" #include <stdio.h> __global__ void printArray(int* array, const int* size) { printf("device memory array : "); for (int i = 0; i < *size; i++) { printf(" %d", array[i]); } printf("\n"); for (int i = 0; i < *size; i++) { array[i] = 5 - i; } } int main() { const int array_size = 5; int array_a[array_size] = { 1, 2, 3, 4, 5 }; int *array_size_device; int* array_a_device; //申请设备内存 cudaMalloc(&array_size_device,sizeof(int)); cudaMalloc(&array_a_device, sizeof(int) * array_size); //将主机内存上的数据拷贝到设备内存 cudaMemcpy(array_size_device, &array_size, sizeof(int), cudaMemcpyHostToDevice); cudaMemcpy(array_a_device, array_a, sizeof(int) * array_size,cudaMemcpyHostToDevice); //调用核函数,打印数组的值,并改变数组的值 printArray <<<1, 1 >>> (array_a_device, array_size_device); int e = cudaGetLastError(); //将设备内存上的数据拷贝回主机内存 cudaMemcpy(array_a, array_a_device, sizeof(int) * array_size, cudaMemcpyDeviceToHost); printf("host memory arry: "); //打印主机内存上的数组 for (int i = 0; i < array_size; i++) { printf(" %d", array_a[i]); } printf("\n"); cudaFree(array_a_device); return 0; }
运行结果:
文章评论
好文,就是有多处 typo:现存 -> 显存,和函数 -> 核函数。
@RsYes 感谢指正,已修改。