1 内存管理
内存分为两种:
CPU及其内存-主机内存
GPU及其内存-设备内存
cuda c的内存管理类似于C语言。
功能 | C | CUDA |
---|---|---|
申请内存 | malloc | cudaMalloc |
初始化内存 | memset | cudaMemset |
申请内存 | free | cudaFree |
复制 | memcpy | cudaMemcpy |
cudaError_t cudaMalloc (void **devPtr, size_t size );
对于cudaMalloc,注意参数的格式,是双重指针。
如果已分配的主机内存A,已分配的主机内存B之间直接赋值A=B会发生错误(cuda 6.0之前)。
需要使用cudaMemcpy(A,B,nBytes,cudaMemcpyDeviceToHost);
其中nBytes是空间的大小,单位为字节,最后一个参数为类型kind,有四种。
1 cudaMemcpyDeviceToHost - GPU->CPU
2 cudaMemcpyHostToDevice - CPU->GPU
3 cudaMemcpyDeviceToDevice - GPU->GPU
4 cudaMemcpyHostToHost - CPU->CPU
cuda 6.0之后提出统一寻址。
cuda采用线程块和线程网络来管理线程。坐标变量有如下两种:
·blockIdx(线程块在线程格内的索引)
·threadIdx(块内的线程索引)
由一个内核启动所产生的所有线程统称为一个网格。同一网格中的所有线程共享相同的全局内存空间。一个网格由多个线程块构成,一个线程块包含一组线程,同一线程块内的线程协作可以通过·同步·共享内存来实现。
不同块内的线程不能协作。
通常,一个线程格会被组织成线程块的二维数组形式,一个线程块会被组织成线程的三维数组形式。
示例代码
#include <stdio.h>
__global__ void checkIndex(){
printf("threadIdx(%d,%d,%d) blockIdx(%d,%d,%d) blockDim(%d,%d,%d) gridDim(%d,%d,%d)\n",
threadIdx.x,threadIdx.y,threadIdx.z,blockIdx.x,blockIdx.y,blockIdx.z,
blockDim.x,blockDim.y,blockDim.z,gridDim.x,gridDim.y,gridDim.z);
}
//打印坐标和size
int main(){
int nElem = 6;//线程总数
dim3 block(3);//dim3类型,3*1*1,用作线程块
dim3 grid((nElem+block.x-1)/block.x);//dim3类型,2*1*1,用作线程网格,包含两个block
//网格大小是块大小的倍数,块大小×网格大小 = 总线程数
printf("grid.x:%d grid.y:%d grid.z:%d\n",grid.x,grid.y,grid.z);
printf("block.x:%d block.y:%d block.z:%d\n",block.x,block.y,block.z);
checkIndex<<<grid,block>>>();
//grid,有几个启动块,每个启动块有几个线程。
cudaDeviceReset();//清除空间
return 0;
cuda的同步异步
核函数的调用与主机线程是异步的,也就是核函数调用完成后,控制权马上返还给主机端CPU。但是可以通过下列函数强制主机端等待所有核函数执行完毕
cudaError_t cudaDeviceSynchronize(void);
一些cuda运行时API在主机和设备之间是隐式同步的。如当使用cudaMemcpy函数进行拷贝的时候,主机端必须等数据拷贝完成才能继续执行。
编写核函数
核函数是在设备端执行的代码。
核函数有以下局限:
1、只能访问设备内存
2、必须有void返回类型
3、不支持可变数量的参数
4、不支持静态变量
5、显示异步行为
CUDA的时间
下面的函数返回从1970年1月1日起,到现在经过的秒数,即系统时间。
double cpuSecond