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 感谢指正,已修改。