常量内存其实只是全局内存的一种虚拟地址形式,并无特殊保留的常量内存块。常量内存有两个特性:c++
常量内存的大小限定为64K,每一个SM拥有8KB的常数存储器缓存,在编译期时声明一块常量内存,须要用到__constant__关键字,例如api
__constant__ float my_array[1024] = { 0.0F, 1.0F, 1.3F, ...};
不一样于c/c++中的const常量,cuda中常量内存在声明后是能够修改的。若是要在运行时改变常量内存中内容,只须要在调用GPU内核以前简单地调用cudaCopyToSymbol函数。若是在编译阶段或主机端运行阶段都没有定义常量内存,那么常量内存区将未定义。数组
使用预约义宏__CUDA_ARCH__来支持主机与设备的常量内存复制,这会方便CPU和GPU对内存的读取。缓存
__constant__ double dc_vals[2] = {0.0, 1000.0}; const double hc_vals[2] = {0.0, 1000.0}; __device__ __host__ double f(size_t i) { #ifdef __CUDA_ARCH__ return dc_vals[i]; #else return hc_vals[i]; #endif }
cuda运行时应用程序可使用函数cudaMemecpyToSymbol()和cudaMemcpyFromSymbol()分别复制数据到常量内存和从常量内存复制数据。常量内存的指针可使用cudaGetSymbolAddress()函数查询。函数
驱动程序api应用程序可使用函数cuModuleGlobal()查询常量内存的设备指针。因为驱动程序api不包括cuda运行时的语言集成特性。驱动程序api不包括像cudaMemcpyToSymbol()这样的特殊内存复制函数。因此必须使用cuModuleGetGlobal()查询地址,以后使用cuMemcpyHtoD()或cuMemcpyDtoH().线程
__constant__ char p_HelloCUDA[11]; __constant__ int t_HelloCUDA[11] = {0,1,2,3,4,5,6,7,8,9,10}; __constant__ int num = 11; __global__ static void HelloCUDA(char * result) { int i = 0; for(i=0; i<num; i++) { result[i] = p_HelloCUDA[i] + t_HelloCUDA[i]; } } int main(int argc, char* argv[]) { if(!InitCUDA()) return 0; char helloCUDA[] = "hdjik CUDA!"; char *device_result = 0; char host_result[12] = {0}; CUDA_SAFE_CALL(cudaMalloc(void**)&device_result, sizeof(char) * 11); CUDA_SAFE_CALL(cudaMemcpyToSymbol(p_HelloCUDA, helloCUDA, sizeof(char) * 11)); HelloCUDA<<<1, 1, 0>>>(device_result); CUT_CHECK_ERROR("Kernel excution failed\n"); CUDA_SAFE_CALL(cudaMemcpy(&host_result, device_result, sizeof(char) * 11, cudaMemcpyDeviceToHost));; printf("%s\n", host_result); CUDA_SAFE_CALL(cudaFree(device_result)); CUT_EXIT(argc, argv); return 0; }
注意到定义常量存储器时,须要将其定义在全部函数以外,做用范围为整个文件,而且对主机端和设备端函数均可见。同时,上述代码中说明了使用常量内存的两种方法。指针
__constant__ int t_HelloCUDA[11] = {0,1,2,3,4,5,6,7,8,9,10}; __constant__ int num = 11;
__constant__ char p_HelloCUDA[11]; // 声明 ... // 使用cudaMemcpyToSymbol进行赋值 CUDA_SAFE_CALL(cudaMemcpyToSymbol(p_HelloCUDA, helloCUDA, sizeof(char) * 11)); ...