GPU包含数百个数学计算单元,具有强大的处理运算能力,可以强大到计算速率高于输入数据的速率,即充分利用带宽,满负荷向GPU传输数据还不够它计算的。CUDA C除全局内存和共享内存外,还支持常量内存,常量内存用于保存在核函数执行期间不会发生变化的数据,使用常量内存在一些情况下,能有效减少内存带宽,降低GPU运算单元的空闲等待。
使用常量内存可以提升运算性能的原因如下:
对常量内存的单次读操作可以广播到其他的“邻近(nearby)”线程,这将节约15次读取操作;高速缓存。常量内存的数据将缓存起来,因此对于相同地址的连续操作将不会产生额外的内存通信量;在CUDA架构中,线程束是指一个包含32个线程的集合,这个线程集合被“编织在一起”并且以“步调一致(Lockstep)”的形式执行。当处理常量内存时,NVIDIA硬件将把单次内存读取操作广播到每个半线程束(Half-Warp)。在半线程束中包含16个线程,即线程束中线程数量的一半。如果在半线程束中的每个线程从常量内存的相同地址上读取数据,那么GPU只会产生一次读取请求并在随后将数据广播到每个线程。如果从常量内存中读取大量数据,那么这种方式产生的内存流量只是使用全局内存时的1/16。#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()。运行效果: 

新闻热点
疑难解答