`

CUDA By Example(三)

    博客分类:
  • GPU
 
阅读更多

        GPU性能的瓶颈往往不在于芯片的数学计算吞吐量,而在于芯片的内存宽带。GPU有非常多的数字逻辑单元(ALU),因此有时输入数据的速率无法维持如此高的计算速率。
         CUDA C除了可以使用全局内存和共享内存,还支持常量内存。常量内存用于保持核函数执行期间不会发生变化的数据。NVIDA提供了64KB的常量内存。在某些情况下,用常量内存替代全局内存能有效减少内存宽带。

 

         下面的例子演示光线跟踪。

         光线跟踪(Ray Tracing)是从三维对象场景中生成二维图像的一种方式。(OpenGL、DirectX中有一种相同目的的技术:光栅化,Rasterization)。光线跟踪的原理是:在场景中选择一个位置放一台假象的相机,这台相机用光传感器来生成图像,图像的每个像素与命中传感器的光线有着相同的颜色和强度。命中的光线可能来自场景中的任意位置,因此采用逆向计算跟容易一些。从像素中投射出的光线穿过场景,直到光像命中某个物体,然后计算这个像素的颜色,这里称像素将“看到”这个物体。光线跟踪中的大部分计算都是光线与场景中物体的相交运算。
         更复杂的光线跟踪模型中,场景中会有反射光线和折射光线出现,这将生成二次射线、三次射线等。

#include "cuda_runtime.h"  
#include "device_launch_parameters.h"  
#include "device_functions.h" 
#include <stdio.h>
#include <math.h>
#include "bitmap.h"

#define INF 2e10f
#define SPHERES 20
#define DIM 1024
#define rnd( x )  (x*rand()/RAND_MAX)

struct Sphere{
	float r,b,g;
	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=sqrtf(radius*radius-dx*dx-dy*dy);
	    	   *n=dz/sqrtf(radius*radius);
	    	   return dz+z;
    	    }
    	    return -INF;
    	 }
    };

__global__ void kernel(unsigned char*ptr ,Sphere *s);

int main(){
	Sphere *s;
	cudaEvent_t start,stop;
	cudaEventCreate(&start);
	cudaEventCreate(&stop);
	cudaEventRecord(start,0);

	Bitmap bitmap(DIM ,DIM);
	unsigned char *dev_bitmap;
	cudaMalloc((void**)&dev_bitmap,bitmap.image_size());  //给dev_bitmap分配内存
	cudaMalloc((void **)&s,sizeof(Sphere)*SPHERES);  //给s分配内存

	Sphere *temp_s=(Sphere*)malloc(sizeof(Sphere)*SPHERES);//在CPU上先给s一个拷贝
	for(int i=0;i<SPHERES;i++){  //生成20个随机数组给temp_s赋值
		temp_s[i].r=rnd(1.0f);
		temp_s[i].g=rnd(1.0f);
		temp_s[i].b=rnd(1.0f);
		temp_s[i].x=rnd(1000.0f)-500;
		temp_s[i].y=rnd(1000.0f)-500;
		temp_s[i].z=rnd(1000.0f)-500;
		temp_s[i].radius=rnd(100.0f)+20;
	}

	cudaMemcpy(s,temp_s,sizeof(Sphere)*SPHERES,cudaMemcpyHostToDevice);
	free(temp_s);

	//数据都分配到GPU上了,接下来启动核函数
	dim3 grids(DIM/16,DIM/16);
	dim3 threads(16,16);
	kernel<<<grids,threads>>>(dev_bitmap,s);

	cudaMemcpy(bitmap.get_ptr(),dev_bitmap,bitmap.image_size(),cudaMemcpyDeviceToHost);

	cudaEventRecord(stop,0);
	cudaEventSynchronize(stop);
	float elapsedTime;
	cudaEventElapsedTime(&elapsedTime,start,stop);
	printf("time to generate:%3.1f 毫秒",elapsedTime);
	cudaEventDestroy(start);
	cudaEventDestroy(stop);

	bitmap.display_and_exit();
	cudaFree(dev_bitmap);
	cudaFree(s);
}


__global__ void kernel(unsigned char*ptr,Sphere *s){
	int x=threadIdx.x+blockDim.x*blockIdx.x;
	int y=threadIdx.y+blockDim.y*blockIdx.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*4+0]=(int)(r*255);
	ptr[offset*4+1]=(int)(g*255);
	ptr[offset*4+2]=(int)(b*255);
	ptr[offset*4+3]=255;
}

 

        程序思路大概如下:数据结构Sphere用来保存球的信息(颜色,位置,半径),Sphere中的hit用来计算(ox,oy)的像素的光线是否与这个球面相交。如果相交,那么将这个方法计算从相机到光线命中球面处的距离。然后随机产生一些Sphere,拷贝到GPU上。kernel是一个简单的光线跟踪模型,hit来判断像素点是否看到Sphere,如果比相机更近,那么作为新的最接近球面。

        运行结果:

 

 

 

接下来是使用常量的版本。这里讲球Sphere数组设置为常量,常量需要使用需要注意的问题在程序中有说明。

#include "cuda_runtime.h"  
#include "device_launch_parameters.h"  
#include "device_functions.h" 
#include <stdio.h>
#include <math.h>
#include "bitmap.h"

#define INF 2e10f
#define SPHERES 200
#define DIM 1024
#define rnd( x )  (x*rand()/RAND_MAX)

struct Sphere{
	float r,b,g;
	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=sqrtf(radius*radius-dx*dx-dy*dy);
	    	   *n=dz/sqrtf(radius*radius);
	    	   return dz+z;
    	    }
    	    return -INF;
    	 }
    };

__global__ void kernel(unsigned char*ptr); //常量这里不需要将指针传递到函数里面去

__constant__ Sphere s[SPHERES];  //常量定义在外面

int main(){
	//Sphere *s;
	cudaEvent_t start,stop;
	cudaEventCreate(&start);
	cudaEventCreate(&stop);
	cudaEventRecord(start,0);

	Bitmap bitmap(DIM ,DIM);
	unsigned char *dev_bitmap;
	cudaMalloc((void**)&dev_bitmap,bitmap.image_size());
	//常量不需要分配内存,变量需要分配内存

	Sphere *temp_s=(Sphere*)malloc(sizeof(Sphere)*SPHERES);
	for(int i=0;i<SPHERES;i++){
		temp_s[i].r=rnd(1.0f);
		temp_s[i].g=rnd(1.0f);
		temp_s[i].b=rnd(1.0f);
		temp_s[i].x=rnd(1000.0f)-500;
		temp_s[i].y=rnd(1000.0f)-500;
		temp_s[i].z=rnd(1000.0f)-500;
		temp_s[i].radius=rnd(100.0f)+20;
	}

	cudaMemcpyToSymbol(s,temp_s,sizeof(Sphere)*SPHERES);  //常量拷贝过程用的函数不一样
	free(temp_s);

	dim3 grids(DIM/16,DIM/16);
	dim3 threads(16,16);
	kernel<<<grids,threads>>>(dev_bitmap);

	cudaMemcpy(bitmap.get_ptr(),dev_bitmap,bitmap.image_size(),cudaMemcpyDeviceToHost);

	cudaEventRecord(stop,0);
	cudaEventSynchronize(stop);
	float elapsedTime;
	cudaEventElapsedTime(&elapsedTime,start,stop);
	printf("time to generate:%3.1f 毫秒",elapsedTime);
	cudaEventDestroy(start);
	cudaEventDestroy(stop);

	bitmap.display_and_exit();
	cudaFree(dev_bitmap);
	//常量不需要free
}

__global__ void kernel(unsigned char*ptr){
	int x=threadIdx.x+blockDim.x*blockIdx.x;
	int y=threadIdx.y+blockDim.y*blockIdx.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;
		}
	}

	ptr[offset*4+0]=(int)(r*255);
	ptr[offset*4+1]=(int)(g*255);
	ptr[offset*4+2]=(int)(b*255);
	ptr[offset*4+3]=255;
}

 

         __constant__把变量的访问限制为只读。与全局内存中读取数据相比,从常量内存中读取相同的数据可以节约内存带宽,因为:对常量内存的单次读取可以广播到其他的“近邻”线程;常量内存的数据将缓存起来,因此对相同地址的连续读操作将不会产生额外的内存通信量。
        这里所谓的“近邻”涉及到“线程束(wrap)”的概念。线程束可以看成是一组线程通过交织而形成的一个整体。CUDA中,线程束是指一个包含32个线程的集合,这个线程集合交织到一起并以步调一致的形式执行。在程序中的每一行,线程束中的每一个线程都将在不同的数据上执行相同的命令。
         当处理常量时,硬件将单次内存读取广播到每半个线程束(包含16个线程),这样,需要的内存流量大概是原始方式的1/16。硬件主动将这个常量数据存在GPU上,在第一次读取常量后,当其他半线程束请求同一数据的地址时,就会命中缓存。

        如果16个线程读取相同地址时候能够极大提升性能,如果16个线程分别读取不同地址可能会降低性能。

 

这里的cudaEvent用来统计执行时间。cudaEventRecord()视为一条记录当前时间的语句。当cudaEventSynchronize()返回时,stop之前的工作就完成了。这时就可以计算时间戳。

CUDA中的event是直接在GPU上实现的,他们不适合计算host与device的混合计时。

 

  • 大小: 167.3 KB
分享到:
评论

相关推荐

    cuda by example.pdf

    《CUDA by Example》是一本介绍通用目的图形处理单元(GPU)编程的教程书籍,作者是Jason Sanders和Edward Kandrot。本书不仅为读者提供了通过实例学习CUDA编程的机会,也是NVIDIA官方推荐的GPU学习教程。它深入浅出...

    CUDA by Example (2010).pdf

    由于给定文件的【标题】和【描述】均重复了"CUDA by Example (2010).pdf",而【标签】是"CUDA by Example",这表明文件的内容应该与这本书——《CUDA by Example》——紧密相关。该书由Jason Sanders和Edward ...

    《CUDA By Example》中文译名《GPU高性能编程CUDA实战》是研究GPGPU异构并行计算非常不错的工具书。

    《CUDA By Example》中文译名《GPU高性能编程CUDA实战》是研究GPGPU异构并行计算非常不错的工具书。书中给出的代码,非常个别的地方有失误,但是都有人为标注了,而且对不同的编程工具可能需要自己配置链接库。...

    CUDA by example (中文:GPU高性能编程CUDA实战)代码实例

    《CUDA by Example》是一本深度探讨GPU高性能编程的权威书籍,中文版名为“GPU高性能编程CUDA实战”。本书通过丰富的实例,详细介绍了CUDA编程模型及其在并行计算中的应用。CUDA,全称为Compute Unified Device ...

    cuda by example中文版

    《CUDA by Example》是一本备受推崇的CUDA编程指南,它为初学者提供了深入理解并掌握CUDA编程技术的宝贵资源。CUDA(Compute Unified Device Architecture)是由NVIDIA推出的一种并行计算平台和编程模型,旨在利用...

    CUDA by Example(英文原书+自带源代码)

    《CUDA by Example》是一本专为想要深入了解GPU编程的IT专业人士准备的书籍,特别是对于那些希望通过CUDA技术提高计算性能的开发者。CUDA(Compute Unified Device Architecture)是NVIDIA公司推出的一种并行计算...

    cuda by example 中英文及代码

    cuda by example 中英文版及代码,中文版名称是 GPU高性能编程CUDA实战,含书签

    《CUDA By Example》中文译名《GPU高性能编程CUDA实战》 源码

    《CUDA By Example》中文译名《GPU高性能编程CUDA实战》 源码 包括 book.h cpu_anim.h cpu_bitmap.h gl_helper.h gpu_anim.h glext.h glut.h

    cuda by example 书中源码

    《CUDA by Example》是一本深度解析CUDA编程的权威著作,由NVIDIA公司的专家Jason Katzan和Erik Nijkamp合著。CUDA(Compute Unified Device Architecture)是NVIDIA推出的用于GPU编程的一种并行计算平台和编程模型...

    CUDA by Example

    ### CUDA by Example: An Introduction to General-Purpose GPU Programming #### 一、概述 《CUDA by Example: An Introduction to General-Purpose GPU Programming》是一本由Jason Sanders和Edward Kandrot共同...

    cuda by example

    ### CUDA by Example: An Introduction to General-Purpose GPU Programming #### 一、概述 《CUDA by Example: An Introduction to General-Purpose GPU Programming》是一本由Jason Sanders和Edward Kandrot共同...

    cuda_by_example

    ### CUDA by Example: Key Insights and Detailed Analysis #### Introduction to CUDA CUDA (Compute Unified Device Architecture) is a parallel computing platform and application programming interface ...

    CUDA by Example: An Introduction to General-Purpose GPU Programming

    《CUDA by Example: An Introduction to General-Purpose GPU Programming》是一本深入浅出介绍CUDA编程的书籍,旨在帮助读者理解并掌握GPU(图形处理器)的通用计算能力。CUDA是NVIDIA公司推出的一种编程模型,它...

    cuda By Example 书中代码

    《CUDA By Example》是一本深度探讨CUDA编程的权威书籍,由NVIDIA官方提供,旨在帮助开发者熟练掌握CUDA编程技术,从而充分利用GPU的并行计算能力。CUDA(Compute Unified Device Architecture)是NVIDIA推出的一种...

    《CUDA BY EXAMPLE》(GPU高性能编程CUDA实战)书中所有例子源码

    《CUDA BY EXAMPLE》是GPU高性能编程领域的一本经典著作,主要介绍了如何利用CUDA技术进行并行计算。CUDA,全称Compute Unified Device Architecture,是NVIDIA公司推出的一种编程模型,允许开发者直接利用图形...

    《GPU高性能编程 CUDA实战》/《CUDA By Example》中的book.h

    书名《GPU高性能编程 CUDA实战》和《CUDA By Example》都是深入探讨CUDA编程技术的经典著作。CUDA(Compute Unified Device Architecture)是由NVIDIA推出的并行计算平台和编程模型,旨在利用图形处理单元(GPU)的...

    《GPU高性能编程CUDA实战(CUDA By Example)》头文件book.h

    《GPU高性能编程CUDA实战(CUDA By Example)》头文件_全书使用的,在163页通过多线程,使用多GPU的地方已完美调试,可运行通过。

    cuda by example an introduction to general purpose gpu programming

    介绍GPU编程,给出了很多CUDA编程的示例

Global site tag (gtag.js) - Google Analytics