CUDA线性内存分配

概述:线性存储器可以通过cudaMalloc()、cudaMallocPitch()和cudaMalloc3D()分配

1、1D线性内存分配

1 cudaMalloc(void**,int)    //在设备端分配内存
2 cudaMemcpy(void* dest,void* source,int size,enum direction)    //数据拷贝
3 cudaMemcpyToSymbol       //将数据复制到__constant__变量中,或者__device__变量中
4 cudaMemcpyFromSynbol   //同上相反
5 cudaFree()               //内存释放
6 cudaMemset()           //内存初始化

注意:主机和设备间的数据交换会自动同步,而设备与设备却不会,需要使用cudaThreadSynchronize()

2、2D线性内存分配

2.1 分配

1 cudaMallocPitch( void** devPtr,size_t* pitch,size_t widthInBytes,size_t height ) //在线性内存中分配二维数组,width的单位是字节,而height单位是数据类型

c语言申请2维内存时,一般是连续存放的。a[y][x]存放在第y*widthofx*sizeof(元素)+x*sizeof(元素)个字节。

在cuda的global memory访问中,从256字节对齐的地址(addr=0, 256, 512, ...)开始的连续访问是最有效率的。这样,为了提高内存访问的效率,有了cudaMallocPitch函数。cudaMallocPitch函数分配的内存中,数组的每一行的第一个元素的开始地址都保证是对齐的。因为每行有多少个数据是不确定的,widthofx*sizeof(元素)不一定是256的倍数。故此,为保证数组的每一行的第一个元素的开始地址对齐,cudaMallocPitch在分配内存时,每行会多分配一些字节,以保证widthofx*sizeof(元素)+多分配的字节是256的倍数(对齐)。这样,上面的y*widthofx*sizeof(元素)+x*sizeof(元素)来计算a[y][x]的地址就不正确了。而应该是y*[widthofx*sizeof(元素)+多分配的字节]+x*sizeof(元素)。而函数中返回的pitch的值就是widthofx*sizeof(元素)+多分配的字节。说明:widthInBytes作为输入参数,应该是widthofx*sizeof(元素);这样的话,复制内容时也要作相应的修改。

2.2 访问

1 T* pElement = (T*)((char*)BaseAddress + Row * pitch) + Column;           //元素访问方式

cudaMallocPitch()以*pitch的形式返回间距,即所分配存储器的宽度,以字节为单位。间距用作存储器分配的一个独立参数,用于在2D数组内计算地址。

2.3 拷贝

1 cudaMemcpy2D( void* dst,size_t dpitch,const void* src,size_t spitch,size_t width,size_t height,enum cudaMemcpyKind kind )

这里需要特别注意width与pitch的区别,width是实际需要拷贝的数据宽度而pitch是2D线性存储空间分配时对齐的行宽,而当数据传递发生在设备与主机之间时,主机端pitch==width.

综上我们可以看到,CUDA下对二维线性空间的访问是不提供多下标支持的,访问时依然是通过计算偏移量得到,不同的地方在于使用pitch对齐后非常利于实现coalesce访

例:下面的代码分配了一个尺寸为width*height的二维浮点数组,同时演示了怎样在设备代码中遍历数组元素

 1 // Host code
 2   int width = 64, height = 64;
 3   float* devPtr;
 4   int pitch;
 5   cudaMallocPitch((void**)&devPtr, &pitch, width * sizeof(float), height); 
 6   MyKernel<<<100, 512>>>(devPtr, pitch, width, height);
 7 // Device code
 8   __global__ void MyKernel(float* devPtr, int pitch, int width, int height){
 9    for (int r = 0; r < height; ++r) {
10       float* row = (float*)((char*)devPtr + r * pitch);
11       for (int c = 0; c < width; ++c) {
12          float element = row[c];
13       }
14    }
15 }

 

3、3D线性内存

1 cudaError_t cudaMalloc3D(    
2     struct cudaPitchedPtr *     pitchedDevPtr,
3     struct cudaExtent             extent     
4 )    

例:下面的代码分配了一个尺寸为width*height*depth的三维浮点数组,同时演示了怎样在设备代码中遍历数组元素

 1 // Host code
 2 cudaPitchedPtr devPitchedPtr;
 3 cudaExtent extent = make_cudaExtent(64, 64, 64);
 4 cudaMalloc3D(&devPitchedPtr, extent);  
 5 MyKernel<<<100, 512>>>(devPitchedPtr, extent);
 6 // Device code
 7 __global__ void MyKernel(cudaPitchedPtr devPitchedPtr, cudaExtent extent) {
 8    char* devPtr = devPitchedPtr.ptr;
 9    size_t pitch = devPitchedPtr.pitch;
10    size_t slicePitch = pitch * extent.height;
11    for (int z = 0; z < extent.depth; ++z) {
12      char* slice = devPtr + z * slicePitch;
13      for (int y = 0; y < extent.height; ++y) {
14         float* row = (float*)(slice + y * pitch); 
15         for (int x = 0; x < extent.width; ++x) { float element = row[x];
16      }
17    }
18 }

 

 

posted @ 2013-04-11 22:12  traceorigin  阅读(2677)  评论(0编辑  收藏  举报