`

CUDA By Example(一)

    博客分类:
  • GPU
 
阅读更多

        cudaMalloc是在设备上分配内存,第一个参数是新分配内存的地址,第二个参数是分配内存大小。在主机上不能对这块内存做任何的修改。主机指针只能访问主机代码中的内存,设备指针只能访问设备代码中的内存。使用完设备内存后要用cudaFree来释放掉分配的内存。在主机代码中可以调用cudaMemcpy来访问设备上的内存。

#include "cuda_runtime.h"
#include "device_launch_parameters.h"
#include<conio.h>
#include <stdio.h>

__global__ void add(int a,int b,int *c){    //设备上运行的函数
	*c=a+b;
}

int main(){
	int c;
	int *dev_c;
	cudaMalloc((void**)&dev_c,sizeof(int));    //在设备上给指针dev_c所指向单元分配内存
	add<<<1,1>>>(2,7,dev_c);    //一个grid中只有一个block,一个block中只有一个thread模式来运行kernel
	cudaMemcpy(&c,dev_c,sizeof(int),cudaMemcpyDeviceToHost);  //设备上dev_c所指内容赋给主机上的c
	printf("2+7=%d",c);
	cudaFree(dev_c);    //释放设备dev_c所指的内存
	getche();
	return 0;
}

 

         尖括号中有两个参数,第一个参数是指设备执行核函数时候使用的并行线程块block的数量。kernel<<<256,1>>>意味着一个Grid中将有256个线程块来执行这个kernel。可以用blockIdx.x来获取线程块的索引。CUDA支持二维线程块数组,在处理矩阵时候或者图形的时候是非常便利的。在启动线程块数组时,数组的每一维数不能超过65335.第二个参数表示运行在每个线程块中创建的线程数量。线程块中的并行线程能够完成并行线程块(block)无法完成的工作。线程块中的线程可以用threadIdx.x来索引。

 

#ifndef _use_h
#define _use_h
    #include "cuda_runtime.h"
    #include "device_launch_parameters.h"
    #include <conio.h>
    #include <stdio.h>
    #define N 100
    __global__ void add(int *a,int *b,int *c);
#endif
#include "use.h"
__global__ void add(int *a,int *b,int *c){
	//int tid=blockIdx.x;    //tid为第i个block
	int tid=threadIdx.x;     //tid为第i个thread
	if(tid<N)
		c[tid]=a[tid]+b[tid];
}

 

#include "use.h"

int main(){
	int a[N],b[N],c[N];
	int *dev_a,*dev_b,*dev_c;

	cudaMalloc((void**)&dev_a,N*sizeof(int));
	cudaMalloc((void**)&dev_b,N*sizeof(int));
	cudaMalloc((void**)&dev_c,N*sizeof(int));

	//CPU上a,b赋值
	for(int i=0;i<N;i++){
		a[i]=-i;
		b[i]=i*i;
	}

	//将a,b拷贝到GPU上
	cudaMemcpy(dev_a,a,N*sizeof(int),cudaMemcpyHostToDevice);
	cudaMemcpy(dev_b,b,N*sizeof(int),cudaMemcpyHostToDevice);

	//add<<<N,1>>>(dev_a,dev_b,dev_c);  //N个block,每个block中一个线程的模式运行add
	add<<<1,N>>>(dev_a,dev_b,dev_c);  //1个block,每个block中有N个线程的模式运行add

	//将c从GPU复制到CPU中
	cudaMemcpy(c,dev_c,N*sizeof(int),cudaMemcpyDeviceToHost);

	for(int i=0;i<N;i++){
		printf("%d+%d=%d\n",a[i],b[i],c[i]);
	}

	//释放GPU上分配的空间
	cudaFree(dev_a);
	cudaFree(dev_b);
	cudaFree(dev_c);
	getche();
}

  

        可以用gridDim.x获取每个grid中有多少block,用blockDim.x来获取每个block中有多少thread。这两个数在线程定位中很有用。以上向量相加并没有考虑线程数量与硬件的一些限制,例如线程格每一位不超过65535。以下对其改进:

#include "use.h"  
__global__ void add(int *a,int *b,int *c){   
    int tid=blockIdx.x*blockDim.x+threadIdx.x;     //第i个block前面有i*blockDim个线程,tid定位到当前的线程
    while(tid<N){
        c[tid]=a[tid]+b[tid];
	    tid+=blockDim.x*gridDim.x;    //如果N相对线程数任然很大,那么可以让第i个线程处理第(i+k*线程数)个线程
	}
} 

 

#include "use.h"

int main(){
	int a[N],b[N],c[N];
	int *dev_a,*dev_b,*dev_c;

	//GPU上分配内存
	cudaMalloc((void**)&dev_a,N*sizeof(int));
	cudaMalloc((void**)&dev_b,N*sizeof(int));
	cudaMalloc((void**)&dev_c,N*sizeof(int));

	//CPU上a,b赋值
	for(int i=0;i<N;i++){
		a[i]=i;
		b[i]=i*i;
	}

	//将a,b拷贝到GPU上
	cudaMemcpy(dev_a,a,N*sizeof(int),cudaMemcpyHostToDevice);
	cudaMemcpy(dev_b,b,N*sizeof(int),cudaMemcpyHostToDevice);

	add<<<128,128>>>(dev_a,dev_b,dev_c);  //128个block,每个block中有128个线程的模式运行add,共16384,约为N的1/3

	//将c从GPU复制到CPU中
	cudaMemcpy(c,dev_c,N*sizeof(int),cudaMemcpyDeviceToHost);
	int success=1;
	for(int i=0;i<N;i++){
		//printf("%d+%d=%d\n",a[i],b[i],c[i]);
		if(a[i]+b[i]!=c[i])
			{success=0;break;}
	}
	if(success==1)
		printf("sucess\n");
	else
		printf("failure\n");

	//释放GPU上分配的空间
	cudaFree(dev_a);
	cudaFree(dev_b);
	cudaFree(dev_c);
	getche();
}

 

        关键字__share__用于变量声明,可以将变量驻留在共享内存中。CUDA C对共享变量与普通变量不同处理。对于共享内存中的变量,编译器会创建它的一个副本,这个block中的每个thread共享这个副本(别的block中的线程不能访问),这样共享内存缓冲区驻留在物理GPU中。访问共享内存的延迟比访问普通缓冲区的延迟要远远低。但线程之间的通信还要实现同步机制。

     同步机制用__syncthreads()来实现。代码中如果使用了这个函数,那么,只有每个线程执行了这个函数以后,才允许进一步执行其余的部分。

    下面这个例子是计算两个向量的内积:

#ifndef _use_h
#define _use_h
    #include "cuda_runtime.h"
    #include "device_launch_parameters.h"
    #include "device_functions.h"
    #include <conio.h>
    #include <stdio.h>
    #include <stdlib.h>
    #define imin(a,b) (a<b?a:b)
    const int N=33*1024;
    const int threadPerBlock=256;
    const int blockPerGrid=imin(32,(N+threadPerBlock-1) / threadPerBlock);
    __global__ void dot(float *a,float *b,float *c);
#endif

 

#include "use.h"

__global__ void dot(float *a,float *b,float *c){
	__shared__ float cache[threadPerBlock];    //共享变量,存储a[i]*b[i]的结果,每个block中都有这个变量
	int tid=threadIdx.x+blockIdx.x*blockDim.x;    //一个grid中的线程号码
	int cacheIndex=threadIdx.x;    //block中的线程编号

	float temp=0;
	while(tid<N){
		temp+=a[tid]*b[tid];    //自己的线程处理的数的和相加
		tid+=blockDim.x*gridDim.x;    //tid加上所以线程数
	}

	cache[cacheIndex]=temp;    //确定block中变量chace的值

	__syncthreads();    //让线程块中的所以线程同步,即让所有线程执行到这个位置

	int i=blockDim.x/2;
	while(i!=0){
		if(cacheIndex<i)
			cache[cacheIndex]+=cache[cacheIndex+i];
		__syncthreads();  //cache[0]到cache[i]的值都确定
		i=i/2;
	}
	if(cacheIndex==0)
		c[blockIdx.x]=cache[0];
}

 

#include "use.h"

int main(){
	float *a,*b,c,*partial_c;  //a,b是要求内积的向量,c是和,partial_c长为grid中的block数,
	                          //partial_c[i]是是第i个block每个线程求得的数据的和
	float *dev_a,*dev_b,*dev_partial_c;

	//在CPU上分配内存
	a=(float*)malloc(N*sizeof(float));
	b=(float*)malloc(N*sizeof(float));
	partial_c=(float*)malloc(blockPerGrid*sizeof(float));

	//在GPU上分配内存
	cudaMalloc((void**)&dev_a,N*sizeof(float));
	cudaMalloc((void**)&dev_b,N*sizeof(float));
	cudaMalloc((void**)&dev_partial_c,blockPerGrid*sizeof(float));

	//填充主机内存
	for(int i=0;i<N;i++){
		a[i]=i;
		b[i]=i*2;
	}

	//将a,b复制到GPU
	cudaMemcpy(dev_a,a,N*sizeof(float),cudaMemcpyHostToDevice);
	cudaMemcpy(dev_b,b,N*sizeof(float),cudaMemcpyHostToDevice);

	dot<<<blockPerGrid,threadPerBlock>>>(dev_a,dev_b,dev_partial_c);

	//复制partial_c到HOST上,在HOST上求和
	cudaMemcpy(partial_c,dev_partial_c,blockPerGrid*sizeof(float),cudaMemcpyDeviceToHost);

	
	c=0;
	for(int i=0;i<blockPerGrid;i++){
		c+=partial_c[i];
	}

		
    #define sum_squares(x) (x*(x+1)*(2*x+1)/6)
    printf("Does GPU value %.6g=%.6g?\n",c,2*sum_squares((float)(N-1)));

	//释放GPU上内存
	cudaFree(dev_a);
	cudaFree(dev_b);
	cudaFree(dev_partial_c);

	free(a);
	free(b);
	free(partial_c);
	getche();
}

     这个求内积过程中抽象为:对于一个输入数组进行某种计算,然后产生一个更小的结果数组,这个过程也成为归约(reduction)。实现归约的最简单的办法是,由一个线程在共享内存上进行迭代并计算出总和值。

     代码中;

int i=blockDim.x/2;
while(i!=0){
    if(cacheIndex<i)
	cache[cacheIndex]+=cache[cacheIndex+i];
    __syncthreads();
    i=i/2;
}
 这里意味着只有部分线程进行工作,而其余的线程总是在进行__syncthreads等待,或许会有以下的优化,即让那些不做工作的线程继续执行至结束,而不是等待,这样或许效率高一些:
int i=blockDim.x/2;
while(i!=0){
    if(cacheIndex<i){
	cache[cacheIndex]+=cache[cacheIndex+i];
        __syncthreads();
    }
    i=i/2;
}

 但是,这是无法工作的,因为,__syncthreads必须保证每个线程执行到这里才会让线程进行下一步工作,但是,有些线程是永远都不会执行__syncthreads(),这样会让程序无尽地等待下去。

 

 

分享到:
评论

相关推荐

    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编程技术的开发者来说,《CUDA by Example》无疑是一个重要的资源。

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

    cuda by example中文版

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

    cuda by example 中英文及代码

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

    cuda by example 书中源码

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

    《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: An Introduction to General-Purpose GPU Programming》是一本由Jason Sanders和Edward Kandrot共同编写的书籍,旨在为读者提供关于CUDA编程的基础知识以及如何利用CUDA进行通用并行计算的实践...

    cuda by example

    《CUDA by Example: An Introduction to General-Purpose GPU Programming》是一本由Jason Sanders和Edward Kandrot共同编写的书籍,主要介绍了如何利用NVIDIA的CUDA技术进行通用并行计算。CUDA(Compute Unified ...

    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)的...

Global site tag (gtag.js) - Google Analytics