GPU包含数百个数学计算单元,具有强大的处理运算能力,可以强大到计算速率高于输入数据的速率,即充分利用带宽,满负荷向GPU传输数据还不够它计算的。CUDA C除全局内存和共享内存外,还支持常量内存,常量内存用于保存在核函数执行期间不会发生变化的数据,使用常量内存在一些情况下,能有效减少内存带宽,降低GPU运算单元的空闲等待。

使用常量内存提升性能

使用常量内存可以提升运算性能的原因如下:

  • 对常量内存的单次读操作可以广播到其他的“邻近(nearby)”线程,这将节约15次读取操作;
  • 高速缓存。常量内存的数据将缓存起来,因此对于相同地址的连续操作将不会产生额外的内存通信量;

在CUDA架构中,线程束是指一个包含32个线程的集合,这个线程集合被“编织在一起”并且以“步调一致(Lockstep)”的形式执行。
当处理常量内存时,NVIDIA硬件将把单次内存读取操作广播到每个半线程束(Half-Warp)。在半线程束中包含16个线程,即线程束中线程数量的一半。如果在半线程束中的每个线程从常量内存的相同地址上读取数据,那么GPU只会产生一次读取请求并在随后将数据广播到每个线程。如果从常量内存中读取大量数据,那么这种方式产生的内存流量只是使用全局内存时的1/16。

常量内存的声明


为普通变量分配内存时是先声明一个指针,然后通过cudaMalloc()来为指针分配GPU内存。而当我们将其改为常量内存时,则要将这个声明修改为在常量内存中静态地分配空间。我们不再需要对变量指针调用cudaMalloc()或者cudaFree(),而是在编译时为这个变量(如一个数组)提交固定的大小。首先用“___constant_”声明一个常量内存变量,然后使用cudaMemcpyToSymbol(而不是cudaMemcpy)把数据从主机拷贝到设备GPU中。

常量内存使用示例


以下程序用CUDA+OpenCv实现一个简单场景的光线跟踪,光线跟踪是从三维场景生成二维图像的一种方式。主要思想为:在场景中选择一个位置放上一台假想的相机,该相机包含一个光传感器来生成图像,需要判断那些光将接触到这个传感器。图像中每个像素与命中传感器的光线有相同的颜色和强度。传感器中命中的光线可能来自场景中的任意位置,想象从该像素发出一道射线进入场景中,跟踪该光线穿过场景,直到光线命中某个物体。代码实现:

#include "cuda_runtime.h"
#include <highgui/highgui.hpp>
#include <time.h> using namespace cv; #define INF 2e10f
#define rnd(x) (x*rand()/RAND_MAX)
#define SPHERES 100 //球体数量
#define DIM 1024 //图像尺寸 struct Sphere
{
float r, g, b;
float radius;
float x, y, z; __device__ float hit(float ox, float oy, float *n)
{
float dx = ox - x;
float dy = oy - y; if (dx*dx + dy*dy < radius*radius)
{
float dz = sqrt(radius*radius - dx*dx - dy*dy);
*n = dz / sqrt(radius*radius);
return dz + z;
} return -INF;
}
}; // Sphere *s;
__constant__ Sphere s[SPHERES]; /************************************************************************/
//__global__ void rayTracing(unsigned char* ptr, Sphere* s)
__global__ void rayTracing(unsigned char* ptr)
{
int x = threadIdx.x + blockIdx.x * blockDim.x;
int y = threadIdx.y + blockIdx.y * blockDim.y;
int offset = x + y * blockDim.x * gridDim.x;
float ox = (x - DIM / 2);
float oy = (y - DIM / 2); float r = 0, g = 0, b = 0;
float maxz = -INF;
for (int i = 0; i < SPHERES; i++)
{
float n;
float t = s[i].hit(ox, oy, &n);
if (t > maxz)
{
float fscale = n;
r = s[i].r * fscale;
g = s[i].g * fscale;
b = s[i].b * fscale;
maxz = t;
}
} ptr[offset * 3 + 2] = (int)(r * 255);
ptr[offset * 3 + 1] = (int)(g * 255);
ptr[offset * 3 + 0] = (int)(b * 255);
}
/************************************************************************/ int main(int argc, char* argv[])
{
cudaEvent_t start, stop;
cudaEventCreate(&start);
cudaEventCreate(&stop);
cudaEventRecord(start, 0); Mat bitmap = Mat(Size(DIM, DIM), CV_8UC3, Scalar::all(0));
unsigned char *devBitmap;
(cudaMalloc((void**)&devBitmap, 3 * bitmap.rows*bitmap.cols));
// cudaMalloc((void**)&s, sizeof(Sphere)*SPHERES); Sphere *temps = (Sphere*)malloc(sizeof(Sphere)*SPHERES); srand(time(0)); //随机数种子 for (int i = 0; i < SPHERES; i++)
{
temps[i].r = rnd(1.0f);
temps[i].g = rnd(1.0f);
temps[i].b = rnd(1.0f);
temps[i].x = rnd(1000.0f) - 500;
temps[i].y = rnd(1000.0f) - 500;
temps[i].z = rnd(1000.0f) - 500;
temps[i].radius = rnd(100.0f) + 20;
} // cudaMemcpy(s, temps, sizeof(Sphere)*SPHERES, cudaMemcpyHostToDevice);
cudaMemcpyToSymbol(s, temps, sizeof(Sphere)*SPHERES);
free(temps); dim3 grids(DIM / 16, DIM / 16);
dim3 threads(16, 16);
// rayTracing<<<grids, threads>>>(devBitmap, s);
rayTracing << <grids, threads >> > (devBitmap); cudaMemcpy(bitmap.data, devBitmap, 3 * bitmap.rows*bitmap.cols, cudaMemcpyDeviceToHost); cudaEventRecord(stop, 0);
cudaEventSynchronize(stop); float elapsedTime;
cudaEventElapsedTime(&elapsedTime, start, stop); printf("Processing time: %3.1f ms\n", elapsedTime); imshow("CUDA常量内存使用示例", bitmap);
waitKey();
cudaFree(devBitmap);
// cudaFree(s);
return 0;
}


程序里生成球体的大小和位置是随机的,为了产生随机数,加入了随机数种子srand()。运行效果: 




CUDA中的常量内存__constant__的更多相关文章

  1. GPU CUDA常量内存使用

    #include <cuda.h> #include <stdio.h> int getMulprocessorCount(){ cudaDeviceProp prop; cu ...

  2. 《GPU高性能编程CUDA实战》第六章 常量内存

    ▶ 本章介绍了常量内存的使用,并给光线追踪的一个例子.介绍了结构cudaEvent_t及其在计时方面的使用. ● 章节代码,大意是有SPHERES个球分布在原点附近,其球心坐标在每个坐标轴方向上分量绝 ...

  3. CUDA: 常量内存与事件

    常量内存: 常量内存用于保存在核函数执行期间不会发生变化的数据,在变量面前添加  __constant__  修饰符: __constant__  Sphere  s[SPHERES]; cudaMe ...

  4. CUDA中多维数组以及多维纹理内存的使用

    纹理存储器(texture memory)是一种只读存储器,由GPU用于纹理渲染的图形专用单元发展而来,因此也提供了一些特殊功能.纹理存储器中的数据位于显存,但可以通过纹理缓存加速读取.在纹理存储器中 ...

  5. 【CUDA 基础】5.0 共享内存和常量内存

    title: [CUDA 基础]5.0 共享内存和常量内存 categories: - CUDA - Freshman tags: - 共享内存 - 常量内存 toc: true date: 2018 ...

  6. Eclipse中的快捷键快速生成常用代码(例如无参、带参构造,set、get方法),以及Java中重要的内存分析(栈、堆、方法区、常量池)

    (一)Eclipse中的快捷键:  ctrl+shift+f自动整理选择的java代码 alt+/ 生成无参构造器或者提升信息 alt+shift+s+o 生成带参构造 ctrl+shift+o快速导 ...

  7. cuda学习3-共享内存和同步

    为什么要使用共享内存呢,因为共享内存的访问速度快.这是首先要明确的,下面详细研究. cuda程序中的内存使用分为主机内存(host memory) 和 设备内存(device memory),我们在这 ...

  8. GPU编程自学7 —— 常量内存与事件

    深度学习的兴起,使得多线程以及GPU编程逐渐成为算法工程师无法规避的问题.这里主要记录自己的GPU自学历程. 目录 <GPU编程自学1 -- 引言> <GPU编程自学2 -- CUD ...

  9. CUDA中关于C++特性的限制

    CUDA中关于C++特性的限制 CUDA官方文档中对C++语言的支持和限制,懒得每次看英文文档,自己尝试翻译一下(没有放lambda表达式的相关内容,太过于复杂,我选择不用).官方文档https:// ...

随机推荐

  1. python 命令行:help(),'more'不是内部或外部命令,也不是可运行的程序或批处理文件

    Python下使用help(dict),显示'more'不是内部或外部命令,也不是可运行的程序或批处理文件,该如何处理? 环境变量设置的问题,进入 Path 的环境变量设置界面,将;%SystemRo ...

  2. android 登录和设置IP/端口功能

    本人第一个Android开发功能:登录以及设置IP/端口. 本人是j2ee开发工程师,所以这个可能有一些处理不太完善的地方,欢迎评论在下面,我会认真改进的. 首先是配置strings.xml文件添加用 ...

  3. win32程序如何改变字体大小颜色

    //设定文字大小和颜色 LOGFONT logfont; //改变输出字体 ZeroMemory(&logfont, sizeof(LOGFONT)); logfont.lfCharSet = ...

  4. [PReact] Reduce the Size of a React App in Two Lines with preact-compat

    Not every app is greenfield, and it would be a shame if existing React apps could not benefit from t ...

  5. HDU 1210 Eddy's 洗牌问题(foj1062) || FOJ1050 Number lengths水

    麻痹,感冒了. ------------------------------------------------感冒了的分割线------------------------------------- ...

  6. Tomcat结合Apache、Nginx实现高性能的web服务器

    一.Tomcat为什么需要与apache.nginx一起结合使用? Tomcat虽然是一个servlet和jsp容器,但是它也是一个轻量级的web服务器.它既可以处理动态内容,也可以处理静态内容.不过 ...

  7. Tricks(四十九)—— 按 batch 访问越界的解决办法

    使用 min 函数指定访问的最终位置,本质上是增加一个条件判断: done = false; batch_size = 10000; idx = 1; while ~done idx_end = mi ...

  8. poj 2965 The Pilots Brothers&#39; refrigerator

    The Pilots Brothers' refrigerator Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 18040 ...

  9. Warning: preg_replace(): Compilation failed: missing terminating ] for character class at offset 10 in

    Warning: preg_replace(): Compilation failed: missing terminating ] for character class at offset 10 ...

  10. springMVC中前台ajax传json数据后台controller接受对象为null

    在jquery的ajax中,如果没加contentType:"application/json",那么data就应该对应的是json对象,反之,如果加了contentType:&q ...