多种CUDA存储单元详解CUDA中的存储单元种类

简介: 多种CUDA存储单元详解CUDA中的存储单元种类
  1. 寄存器
  2. 局部存储器
  3. 共享存储器
  4. 全局存储器
  5. 主机端内存
  6. 主机端页锁定内存
  7. 常数存储器
  8. 纹理存储器
存储器 位置 拥有缓存 访问权限 变量生存周期
register GPU (芯)片内 N/A device 可读/写 与thread相同
local memory 板载显存 device 可读/写 与thread相同
shared memory GPU 片内 N/A device 可读/写 与block相同
constant memory 板载显存 device 可读,host可读/写 可在程序中保持
texture memory 板载显存 device 可读,host可读/写 可在程序中保持
global memory 板载显存 device 可读/写,host可读/写 可在程序中保持
host memory host 内存 host 可读/写 可在程序中保持
pinned memory host 内存 host 可读/写 可在程序中保持

共享存储器

示例:共享存储器的动态与静态分配与初始化

int main(int argc, char** argv){

   testKernel<<<1, 10, mem_size>>>(d_idata, d_odata);

   CUT_EXIT(argc, argv);

}

__global__ void testKernel(float* g_idata, float* g_odata){

   extern __shared__ float sdata_dynamic[]; //extern 声明,大小由主机端程序决定。动态声明

   __shared__ int sdata_static[16]; //静态声明数组大小

 

   sdata_static[tid] = 0; //shared memory 不能在定义时初始化

}

将共享存储器中的变量声明为外部数组时,数组的大小将在Kernel 启动时确定,通过其执行参数确定。通过这种方式定义的所有变量都开始于相同的地址,因此数组中的变量的布局必须通过偏移量显式管理。例:如果希望在动态分配的共享存储器内获得与以下代码对应的内容:

short array0[128];

float array1[64];

int array2[256];

应该按照下面的方式定义:

extern __shared__ char array[];

__device__ void func()

{

short* array0 = (short*)array;

float* array1 = (float*)&array0[128];

int* array2 = (int*)&array1[64];

}

全局存储器

       显存中的全局存储器也称为线性内存。线性内存通常使用 cudaMalloc() 函数分配, cudaFree() 函数释放,并由 cudaMemcpy() 进行主机端与设备端的数据传输。通过CUDA API分配的空间未经过初始化,初始化全局存储器需要调用 cudaMemset 函数。

对于二维、三维数组,我们使用 cudaMallocPitch() 和 cudaMalloc3D() 分配线性存储空间。这些函数能够确保分配满足对齐要求。

例:分配一个尺寸为 width * height 的 float 型2D 数组,以及遍历数组元素。

//主机端代码

float* devPtr;

int pitch;

cudaMallocPitch((void**)&devPtr, &pitch, width * sizeof(float), height);

myKernel<<<100, 512>>>(devPtr, pitch);

//设备端代码

__global__ void myKernel(float* devPtr, int pitch){

     for (int r = 0; r < height; ++r){

           float* row = (float*)((char*)devPtr + r * pitch);

                  for (int c = 0; c < width; ++c){

               float element = row[c];

                  }

       }

}

例:分配一个 width * height * depth 的 float 型3D 数组,以及遍历数组元素。

//主机端代码

cudaPitchedPtr devPitchedPtr;

cudaExtent extent = make_cudaExtent(64, 64, 64);

cudaMalloc3D(&devPitchedPtr, extent);

myKernel<<<100, 512>>>(devPitchedPtr, extent);

 

//设备端代码

__global__ void myKernel(cudaPitchedPtr devPitchedPtr, cudaExtent extent){

 char* devPtr = devPitchedPtr.ptr;

               size_t pitch = devPitchedPtr.pitch;

  size_t slicePitch = pitch * extent.height;

 for (int z = 0; z < extent.depth; ++z){

 char* slice = devPtr + z * slicePitch;

                               for (int y = 0; y < extent.height; ++y){

                                                float* row = (float*)(slice + y * pitch);

                               for (int x = 0; x < extent.width; ++x){

                                               float element = row[x];

                                                }

                                  }

                   }

}

例:二维数组和CUDA数组间的数据拷贝。

cudaMemcpy2DToArray(cuArray, 0, 0, devPtr, pitch,  width * sizeof(float), height,    cudaMemcpyDeviceToDevice);

主机端页锁定内存

         通过 cudaHostAlloc() 和 cudaFreeHost() 来分配和释放 pinned memory。

常数存储器

         定义常数存储器时,需要将其定义在所有函数之外,作用范围为整个文件,并且对主机端和设备端函数都可见。下面两段代码说明了两种常数存储器的使用方法。

第一种方法是直接在定义时直接初始化常熟存储器,然后再Kernel里面直接使用就可以了。

__constant__ int t_HelloCUDA[11] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10};    __constant__ int num = 11

第二种方法是定义一个costant 数组,然后使用函数进行赋值。

      __constant__ char p_HelloCUDA[11];

      CUDA_SAFE_CALL(cudaMemcpyToSymbol(p_HelloCUDA, helloCUDA, sizeof(char) * 11));

纹理存储器

在Kernel 中访问纹理存储器的操作称为纹理拾取(texture fetching)。纹理拾取使用的坐标与数据在显存中的位置可以不同,我们通过纹理参照系(texture reference)约定二者的映射方式。将显存中的数据与纹理参照系关联的操作,称为将数据与纹理绑定(texture binding)。显存中可以绑定到纹理的数据有两种,分别是普通的线性存储器(Linear Memory)和 CUDA 数组(CUDA Array)。

纹理存储器的使用

  • 声明CUDA数组,分配空间
  • 声明纹理参照系
  • 设置运行时纹理参照系属性
  • 纹理绑定
  • 纹理拾取

例:简单的纹理使用。

/*

声明纹理参照系:texture<Type, Dim, ReadMode> texRef;

*/

//2D float texture

texture<float, 2, cudaReadModeElementType> texRef;

//设备端代码,一个简单的转换kernel

__global__ void transformKernel(float* output, int width, int height, float theta){

//计算归一化的纹理坐标

unsigned int x = blockIdx.x * blockDim.x + threadIdx.x;

unsigned int y = blockIdx.y * blockDim.y + threadIdx.y;

float u = x / (float)width;

float v = y / (float)height;

//坐标转换

u -= 0.5f;

v -= 0.5f;

float tu = u * cosf(theta) - v * sinf(theta) + 0.5f;

float tv = v * cosf(theta) + u * sinf(theta) + 0.5f;

//读纹理并向全局存储器写会

output[y * width + x] = tex2D(tex, tu, tv);

}

 

//主机端代码

int main()

{

//在先存上为CUDA array 分配空间

cudaChannelFormatDesc channelDesc = cudaCreatChannelDesc(32, 0, 0, 0, cudaChannelFormatKindFloat);

cudaArray* cuArray;

cudaMallocArray(&cuArray, &channelDesc, width, height);

//内存中h_data地址处的数据向显存进行拷贝

cudaMemcpyToArray(cuArray, 0, 0, h_data, size, cudaMemcpyHostToDevice);

//设置纹理参数

texRef.addressMode[0] = cudaAddressModeWrap;

texRef.addressMode[1] = cudaAddressModeWrap;

texRef.filterMode = cudaFilterModeLinear;

texRef.normalized = true;

//数组绑定到纹理

cudaBindTextureToArray(texRef, cuArray, &channelDesc);

//转换结果分配显存空间

float* output;

cudaMalloc((void**)&output, width * height * sizeof(float));

//启动 kernel

dim3 dimBlock(16, 16);

dim3 dimGrid((width + dimBlock.x - 1) / dimBlock.x, (height + dimBlock.y - 1) / dimBlock.y);

transformKernel<<<dimGrid, dimBlock>>>(output, width, height, angle);

//释放显存空间

cudaFreeArray(cuArray);

cudaFree(output);

}


目录
相关文章
|
3天前
|
存储 编解码 算法
没有DSP的浮点单元不能进行浮点运算?
没有DSP的浮点单元不能进行浮点运算?
|
2天前
|
存储 芯片 内存技术
存储器的分类
存储器的分类
12 1
|
3天前
|
存储 芯片 内存技术
嵌入式系统中常见内存的划分方法
嵌入式系统中常见内存的划分方法
82 1
|
3天前
|
并行计算 C++ 异构计算
cuda中关于占用率的计算
cuda中关于占用率的计算
|
并行计算 TensorFlow 算法框架/工具
CUDA存储单元的使用GPU存储单元的分配与释放
CUDA存储单元的使用GPU存储单元的分配与释放
553 0
|
并行计算 异构计算
如何将cuda上的变量转到cpu上面?
在这个示例中,我们首先将x张量对象创建在GPU上。然后,我们使用.cpu()方法将其移动到CPU上,并将其分配给一个新的变量x_cpu。现在,我们可以在CPU上使用x_cpu变量并打印它。 请注意,将张量移动到不同的设备(如从GPU到CPU)可能会涉及到数据的复制,因此需要确保不会频繁地在不同的设备之间移动数据以避免性能下降。
1507 0
|
编译器 C++
结构体的初步认识以及其内存的计算
结构体的初步认识以及其内存的计算
90 0
结构体的初步认识以及其内存的计算
不同CPU指令的指令集密度
不同CPU指令的指令集密度
105 0
不同CPU指令的指令集密度
|
存储 缓存 并行计算
多种CUDA存储单元详解CUDA中的各种存储单元
多种CUDA存储单元详解CUDA中的各种存储单元
446 0
多种CUDA存储单元详解CUDA中的各种存储单元
|
存储 缓存 并行计算
CUDA存储单元的使用GPU的存储单元
CUDA存储单元的使用GPU的存储单元
725 0
CUDA存储单元的使用GPU的存储单元