欢迎您访问 最编程 本站为您分享编程语言代码,编程技术文章!
您现在的位置是: 首页

CUDA 编程学习 <2> - 7 种还原算法优化方法 - 代码部分

最编程 2024-04-25 22:12:32
...

3.1、优化1:交错寻址(Interleaved addressing divergent branches)

3.1.1、核函数(优化1、优化2、优化3):


__global__ void reduce0(unsigned int *g_idata, long long int *g_odata, int N, clock_t* time,int GuiYueWay) //
{//包含归约1、优化2、优化3的程序。
	//__shared__ int sdata[threadsPerBlock];//共享内存,开辟2的指数个空间,2、4、8、32 ...

	//__shared__ unsigned int sdata[threadsPerBlock];//共享内存,开辟2的指数个空间,2、4、8、32 ...
	extern __shared__ long long int sdata[];//动态申请共享内存,空间大小在三个尖括号的第三个参数进行传入<<<1,2,3>>>开辟2的指数个空间,2、

	clock_t start = clock();
	// each thread loads one element from global to shared mem
	unsigned int tid = threadIdx.x;
	unsigned int i = blockIdx.x*blockDim.x + threadIdx.x;
	//printf("tid:%d,i:%d\n",tid, i);

	//方法1,要求一次性把线程数量建完,这样不确定有没有所有最大的线程限定,特点是速度快
	sdata[tid] = (i < N) ? g_idata[i] : 0;
	//printf("CUDA1 i:%d\n", i);

	方法2,可以一个线程多次处理,但是在数据较大的时候结果容易出错
	//unsigned int temp = 0;
	//while (i < N)//N
	//{//这种方式可以一个线程多次处理
	//	temp += g_idata[i];
	//	i += blockDim.x * gridDim.x;
	//	//printf("tid:%d\n", tid);
	//}
	设置cache中相应位置上的值
	//sdata[tid] = temp;

	__syncthreads();//同步共享变量


	switch (GuiYueWay)
	{
	case 1:
		GuiYue_Opt1(sdata, tid);//初始版本的归约函数
		break;
	case 2:
		GuiYue_Opt2(sdata, tid);//优化的第2版本的归约函数
		break;
	case 3:
		GuiYue_Opt3(sdata, tid);//优化的第3版本的归约函数
		break;
	default:
		break;
	}
	// //do reduction in shared mem
	//for (unsigned int s = 1; s < blockDim.x; s *= 2) {
	//	if (tid % (2 * s) == 0) {
	//		sdata[tid] += sdata[tid + s];
	//	}
	//	__syncthreads();
	//}
	// write result for this block to global mem
	//printf("tid2:%d,i2:%d\n", tid, i);

	if (tid == 0)
	{
		g_odata[blockIdx.x] = sdata[0];
		//printf("sdata[0]:%u\n", sdata[0]);
		//printf("g_odata[%d]:%u\n", blockIdx.x, sdata[0]);

	}
	*time = clock() - start;//计时
}

核函数说明
需要说明的是里面用到了共享内存,共享内存的开辟有两种方式,静态定义和动态开辟,可以参考如下链接:
CUDA之静态、动态共享内存分配详解
简要介绍共享内存

// __shared__  为共享内存标识

//动态定义如下,在核函数内部定义,
extern __shared__ unsigned int sdata[];//动态申请共享内存,空间大小在核函数三个尖括号的第三个参数进行传入<<<1,2,3>>>,如需开辟20个空间,则第三个参数应该是20*sizeof(unsigned int)


//静态定义
__shared__ unsigned int sdata[20];//静态开辟20个静态空间

3.1.2、交错寻址函数代码:

__device__ void GuiYue_Opt1(long long int* sdata, unsigned int tid)
{//归约的方式1,交错寻址
	for (unsigned int s = 1; s < blockDim.x; s *= 2) {
		if (tid % (2 * s) == 0) {
			sdata[tid] += sdata[tid + s];
		}
		__syncthreads();
	}
}

3.2、优化2:跨步寻址(Interleaved addressing bank conflicts)

3.2.1、核函数

核函数使用3.1交错寻址的核函数的switch语句调用

3.2.2、跨步寻址函数代码:

__device__ void GuiYue_Opt2(long long int* sdata, unsigned int tid)
{//归约的方式2,跨步寻址
	for (unsigned int s = 1; s < blockDim.x; s *= 2) {
		int index = 2 * s*tid;
		if (index<blockDim.x) {
			sdata[index] += sdata[index + s];
		}
		__syncthreads();
	}
}

3.3、优化3:连续寻址(Sequential Addressing)

3.1、核函数

核函数使用3.1交错寻址的核函数的switch语句调用

3.2、连续寻址函数代码:

__device__ void GuiYue_Opt3(long long int* sdata, unsigned int tid)
{//归约的方式3,连续寻址
	for (unsigned int s = blockDim.x / 2;s>0; s >>= 1) //注意:>>右移1位,假设s=5,那么二进制为0101 s>>1 右移1位,即把最右边的1位删掉,变为010 此时结果为2;如果s=5,s>>2,即右移2位,变成01,结果为1
	{
		if (tid < s) {
			sdata[tid] += sdata[tid + s];
		}
		__syncthreads();
	}
}

3.4、优化4:把一次归约放在加载时进行(first add during load)

3.4.1、核函数:

主要还是用优化3的代码算法,更改核函数的赋值共享内存里,核函数代码如下


__global__ void reduce4( unsigned int *g_idata, long long int *g_odata, int N, clock_t* time, int GuiYueWay) //
{//在优化3的基础上进行优化4和优化5的程序。
	//__shared__ int sdata[threadsPerBlock];//共享内存,开辟2的指数个空间,2、4、8、32 ...
	//__shared__ unsigned int sdata[threadsPerBlock];//全局变量定义共享内存空间,开辟2的指数个空间,2、4、8、32 ...
	extern __shared__ long long int sdata[];//动态申请共享内存,空间大小在三个尖括号的第三个参数进行传入<<<1,2,3>>>开辟2的指数个空间。当数据过大容易越界,需定义大点的空间
	clock_t start = clock();
	// each thread loads one element from global to shared mem
	unsigned int tid = threadIdx.x;
	unsigned int i = blockIdx.x*(blockDim.x*2) + threadIdx.x;//优化位置1
	//printf("tid:%d,i:%d\n",tid, i);
	//sdata[tid] = (i < N) ? g_idata[i] : 0;
	sdata[tid] = (i < N) ? g_idata[i]+ g_idata[i + blockDim.x] : 0;//优化位置2,把第一次归约放在加载数据的时候进行,减少了一次CUDA内部操作
	//printf("CUDA4 i:%d,2i:%d\n", i, i + blockDim.x);
	__syncthreads();//同步共享变量
	switch (GuiYueWay)
	{
	case 4:
		GuiYue_Opt3(sdata, tid);//优化的第3版本的归约函数
		break;
	case 5:
		GuiYue_Opt5(sdata, tid);//优化的第5版本的归约函数
		break;
	default:
		break;
	}
	//GuiYue_Opt3(sdata, tid);//优化的第3版本的归约函数
	if (tid == 0)
	{
		g_odata[blockIdx.x] = sdata[0];
		//printf("g_odata[%d]:%u\n", blockIdx.x,sdata[0]);
	}
	*time = clock() - start;//计时
}

3.4.2 算法优化部分

unsigned int i = blockIdx.x*(blockDim.x*2) + threadIdx.x;//优化位置1
sdata[tid] = (i < N) ? g_idata[i]+ g_idata[i + blockDim.x] : 0;//优化位置2,把第一次归约放在加载数据的时候进行,减少了一次CUDA内部操作

如PPT
在这里插入图片描述

3.5、优化5:最后线程束展开(Unroll the last warp)

5.1 核函数

核函数使用3.4优化4的核函数的switch语句调用

5.2 最后线束展开函数代码:


__device__ void warpReduce(volatile long long int* sdata,int tid)
{//该函数注意volatile,如果没有这个关键字,编译器可能会将sdata变量优化掉,导致结果出错的
	sdata[tid] += sdata[tid + 32];
	sdata[tid] += sdata[tid + 16];
	sdata[tid] += sdata[tid + 8];
	sdata[tid] += sdata[tid + 4];
	sdata[tid] += sdata[tid + 2];
	sdata[tid] += sdata[tid + 1];

}

__device__ void GuiYue_Opt5(long long int* sdata, unsigned int tid)
{//归约的方式5,该函数在归约方式3的基础上进行优化,省去warp过程中不必要的操作
	for (unsigned int s = blockDim.x / 2; s > 32; s >>= 1) //注意:>>右移1位,假设s=5,那么二进制为0101 s>>1 右移1位,即把最右边的1位删掉,变为010 此时结果为2;如果s=5,s>>2,即右移2位,变成01,结果为1
	{
		if (tid < s) {
			sdata[tid] += sdata[tid + s];
		}
		__syncthreads();
	}
	if (tid < 32) 
	{
		warpReduce(sdata, tid); 
	}
}

3.6、优化6:完全展开(completely Unrolled)

3.6.1、核函数:

把核函数优化成了模板方法增加通用性,另外增加了warpReduceT模板函数

//下面函数优化程序6使用
template<unsigned int blockSize>
__device__ void warpReduceT(volatile long long int* sdata, unsigned int tid)
{//该函数注意volatile,如果没有这个关键字,编译器可能会将sdata变量优化掉,导致结果出错的
	if (blockSize >= 64)sdata[tid] += sdata[tid + 32];
	if (blockSize >= 32)sdata[tid] += sdata[tid + 16];
	if (blockSize >= 16)sdata[tid] += sdata[tid + 8];
	if (blockSize >= 8)sdata[tid] += sdata[tid + 4];
	if (blockSize >= 4)sdata[tid] += sdata[tid + 2];
	if (blockSize >= 2)sdata[tid] += sdata[tid + 1];
}

template <unsigned int blockSize>
__global__ void reduce6( unsigned int *g_idata, long long int *g_odata, int N, clock_t* time) //
{//优化6程序
	//__shared__ int sdata[threadsPerBlock];//共享内存,开辟2的指数个空间,2、4、8、32 ...
	//__shared__ unsigned int sdata[threadsPerBlock];//共享内存,开辟2的指数个空间,2、4、8、32 ...
	extern __shared__ long long int sdata[];//动态申请共享内存,空间大小在三个尖括号的第三个参数进行传入<<<1,2,3>>>开辟2的指数个空间,2、4、8、32 ...
	clock_t start = clock();
	// each thread loads one element from global to shared mem
	unsigned int tid = threadIdx.x;
	unsigned int i = blockIdx.x*(blockDim.x * 2) + threadIdx.x;
	//printf("tid:%d,i:%d\n",tid, i);
	//sdata[tid] = (i < N) ? g_idata[i] : 0;
	sdata[tid] = (i < N) ? g_idata[i] + g_idata[i + blockDim.x] : 0;//把第一次归约放在加载数据的时候进行,减少了一次CUDA内部操作

	__syncthreads();//同步共享变量

	//优化的第6版本的归约函数
	if (blockSize >= 512)
	{
		if (tid < 256) { sdata[tid] += sdata[tid + 256]; }__syncthreads();
	}
	if (blockSize >= 256)
	{
		if (tid < 128) { sdata[tid] += sdata[tid + 128]; }__syncthreads();
	}
	if (blockSize >= 128)
	{
		if (tid < 64) { sdata[tid] += sdata[tid + 64]; }__syncthreads();
	}
	if (tid < 32)warpReduceT<blockSize>(sdata, tid);

	if (tid == 0)
	{
		g_odata[blockIdx.x] = sdata[0];
		//printf("sdata[0]:%d\n", sdata[0]);
	}
	*time = clock() - start;//计时
}

7、优化7:每个线程有多个元素(Multiple elements per thread)

与第6个优化版本相比,在核函数的时候就进行了合并,另外使用了优化6中的warpReduceT模板函数


template <unsigned int blockSize>
__global__ void reduce7(unsigned int *g_idata,long long int *g_odata, int N, clock_t* time) //
{//优化6程序
	//__shared__ int sdata[threadsPerBlock];//共享内存,开辟2的指数个空间,2、4、8、32 ...
	//__shared__ unsigned int sdata[threadsPerBlock];//共享内存,开辟2的指数个空间,2、4、8、32 ...
	extern __shared__ long long int sdata[];//动态申请共享内存,空间大小在三个尖括号的第三个参数进行传入<<<1,2,3>>>开辟2的指数个空间,2、4、8、32 ...
	clock_t start = clock();
	// each thread loads one element from global to shared mem
	unsigned int tid = threadIdx.x;
	unsigned int i = blockIdx.x*(blockSize * 2) + threadIdx.x;//更改位置1
	unsigned int gridSize = blockSize * 2 * gridDim.x;
	sdata[tid] = 0;
	//printf("tid:%d,i:%d\n",tid, i);
	//sdata[tid] = (i < N) ? g_idata[i] : 0;
	//sdata[tid] = (i < N) ? g_idata[i] + g_idata[i + blockDim.x] : 0;//把第一次归约放在加载数据的时候进行,减少了一次CUDA内部操作
	while (i<N)
	{
		sdata[tid] += g_idata[i] + g_idata[i + blockSize];
		i += gridSize;
	}
	__syncthreads();//同步共享变量

	//优化的第6版本的归约函数
	if (blockSize >= 512)
	{
		if (tid < 256) { sdata[tid] += sdata[tid + 256]; }__syncthreads();
	}
	if (blockSize >= 256)
	{
		if (tid < 128) { sdata[tid] += sdata[tid + 128]; }__syncthreads();
	}
	if (blockSize >= 128)
	{
		if (tid < 64) { sdata[tid] += sdata[tid + 64]; }__syncthreads();
	}
	if (tid < 32)warpReduceT<blockSize>(sdata, tid);//

	if (tid == 0)
	{
		g_odata[blockIdx.x] = sdata[0];
		//printf("sdata[0]:%d\n", sdata[0]);
	}
	*time = clock() - start;//计时
}

英文pdf上有几处错误和需要注意的地方:
优化7最后的这个代码,因为是调用模板方法,所以需要增加模板值
在这里插入图片描述

8、汇总以上所有测试代码,包括主函数

/*
归约样例,增加时间统计,关注宽带占用情况
参考链接:
中文:https://wenku.baidu.com/link?url=LOvDSVU9fyqQko7h4rIWEuxwmKCgCOzrFSGoE1saaNWpGlmfl2wR9Qu--IcdwM9D0H1cIT_0mUFLrUazYPFuUrc57O_RbD8_IbS27MjFmtS
英文原版:https://developer.download.nvidia.cn/assets/cuda/files/reduction.pdf


优化方式:
归约的方式1,交错寻址
归约的方式2,跨步寻址
归约的方式3,连续寻址
归约的方式4,在连续寻址基础上进行改进。由于第一次循环迭代中,一半线程处于空闲状态,太浪费了,所以优化。比前三种优化,每个共享内存中的数值大约是
归约的方式5,在最后进行了swarp展开,实测速度不如第四次快
归约的方式6,完全展开,把核函数写成模板方法进行调用,在优化部分进行了模板方法的完全展开。模板方法会在编译的时候进行评估计算
归约的方式7,把第4种方法和第6种方法结合。


*/



///*

#include "device_launch_parameters.h"
#include "cuda_runtime.h"
#include "device_functions.h"
#include <stdio.h>
#include <iostream>
#include <time.h>

#define imin(a,b) (a<b?a:b)


//#define N  131072*2//1048576 //数组长度,总任务  1024//
//#define threadsPerBlock  128//后面执行归约并行计算,该值必须是2的指数值
//#define blocksPerGrid 32//规约后的长度







__device__ void GuiYue_Opt1(long long int* sdata, unsigned int tid)
{//归约的方式1,交错寻址
	for (unsigned int s = 1; s < blockDim.x; s *= 2) {
		if (tid % (2 * s) == 0) {
			sdata[tid] += sdata[tid + s];
		}
		__syncthreads();
	}
}

__device__ void GuiYue_Opt2(long long int* sdata, unsigned int tid)
{//归约的方式2,跨步寻址
	for (unsigned int s = 1; s < blockDim.x; s *= 2) {
		int index = 2 * s*tid;
		if (index<blockDim.x) {
			sdata[index] += sdata[index + s];
		}
		__syncthreads();
	}
}


__device__ void GuiYue_Opt3(long long int* sdata, unsigned int tid)
{//归约的方式3,连续寻址
	for (unsigned int s = blockDim.x / 2;s>0; s >>= 1) //注意:>>右移1位,假设s=5,那么二进制为0101 s>>1 右移1位,即把最右边的1位删掉,变为010 此时结果为2;如果s=5,s>>2,即右移2位,变成01,结果为1
	{
		if (tid < s) {
			sdata[tid] += sdata[tid + s];
		}
		__syncthreads();
	}
}

__device__ void warpReduce(volatile long long int* sdata,int tid)
{//该函数注意volatile,如果没有这个关键字,编译器可能会将sdata变量优化掉,导致结果出错的
	sdata[tid] += sdata[tid + 32];
	sdata[tid] += sdata[tid + 16];
	sdata[tid] += sdata[tid + 8];
	sdata[tid] += sdata[tid + 4];
	sdata[tid] += sdata[tid + 2];
	sdata[tid] += sdata[tid + 1];

}

__device__ void GuiYue_Opt5(long long int* sdata, unsigned int tid)
{//归约的方式5,该函数在归约方式3的基础上进行优化,省去warp过程中不必要的操作
	for (unsigned int s = blockDim.x / 2; s > 32; s >>= 1) //注意:>>右移1位,假设s=5,那么二进制为0101 s>>1 右移1位,即把最右边的1位删掉,变为010 此时结果为2;如果s=5,s>>2,即右移2位,变成01,结果为1
	{
		if (tid < s) {
			sdata[tid] += sdata[tid + s];
		}
		__syncthreads();
	}
	if (tid < 32) 
	{
		warpReduce(sdata, tid); 
	}
}


//下面函数优化程序6使用
template<unsigned int blockSize>
__device__ void warpReduceT(volatile long long int* sdata, unsigned int tid)
{//该函数注意volatile,如果没有这个关键字,编译器可能会将sdata变量优化掉,导致结果出错的
	if (blockSize >= 64)sdata[tid] += sdata[tid + 32];
	if (blockSize >= 32)sdata[tid] += sdata[tid + 16];
	if (blockSize >= 16)sdata[tid] += sdata[tid + 8];
	if (blockSize >= 8)sdata[tid] += sdata[tid + 4];
	if (blockSize >= 4)sdata[tid] += sdata[tid + 2];
	if (blockSize >= 2)sdata[tid] += sdata[tid + 1];
}



__global__ void reduce0(unsigned int *g_idata, long long int *g_odata, int N, clock_t* time,int GuiYueWay) //
{//包含归约1、优化2、优化3的程序。
	//__shared__ int sdata[threadsPerBlock];//共享内存,开辟2的指数个空间,2、4、8、32 ...

	//__shared__ unsigned int sdata[threadsPerBlock];//共享内存,开辟2的指数个空间,2、4、8、32 ...
	extern __shared__ long long int sdata[];//动态申请共享内存,空间大小在三个尖括号的第三个参数进行传入<<<1,2,3>>>开辟2的指数个空间,2、

	clock_t start = clock();
	// each thread loads one element from global to shared mem
	unsigned int tid = threadIdx.x;
	unsigned int i = blockIdx.x*blockDim.x + threadIdx.x;
	//printf("tid:%d,i:%d\n",tid, i);

	//方法1,要求一次性把线程数量建完,这样不确定有没有所有最大的线程限定,特点是速度快
	sdata[tid] = (i < N) ? g_idata[i] : 0;
	//printf("CUDA1 i:%d\n", i);

	方法2,可以一个线程多次处理,但是在数据较大的时候结果容易出错
	//unsigned int temp = 0;
	//while (i < N)//N
	//{//这种方式可以一个线程多次处理
	//	temp += g_idata[i];
	//	i += blockDim.x * gridDim.x;
	//	//printf("tid:%d\n", tid);
	//}
	设置cache中相应位置上的值
	//sdata[tid] = temp;

	__syncthreads();//同步共享变量


	switch (GuiYueWay)
	{
	case 1:
		GuiYue_Opt1(sdata, tid);//初始版本的归约函数
		break;
	case 2:
		GuiYue_Opt2(sdata, tid);//优化的第2版本的归约函数
		break;
	case 3:
		GuiYue_Opt3(sdata, tid);//优化的第3版本的归约函数
		break;
	default:
		break;
	}
	// //do reduction in shared mem
	//for (unsigned int s = 1; s < blockDim.x; s *= 2) {
	//	if (tid % (2 * s) == 0) {
	//		sdata[tid] += sdata[tid + s];
	//	}
	//	__syncthreads();
	//}
	// write result for this block to global mem
	//printf("tid2:%d,i2:%d\n", tid, i);

	if (tid == 0)
	{
		g_odata[blockIdx.x] = sdata[0];
		//printf("sdata[0]:%u\n", sdata[0]);
		//printf("g_odata[%d]:%u\n", blockIdx.x, sdata[0]);

	}
	*time = clock() - start;//计时
}

__global__ void reduce4( unsigned int *g_idata, long long int *g_odata, int N, clock_t* time, int GuiYueWay) //
{//在优化3的基础上进行优化4和优化5的程序。
	//__shared__ int sdata[threadsPerBlock];//共享内存,开辟2的指数个空间,2、4、8、32 ...
	//__shared__ unsigned int sdata[threadsPerBlock];//全局变量定义共享内存空间,开辟2的指数个空间,2、4、8、32 ...
	extern __shared__ long long int sdata[];//动态申请共享内存,空间大小在三个尖括号的第三个参数进行传入<<<1,2,3>>>开辟2的指数个空间。当数据过大容易越界,需定义大点的空间
	clock_t start = clock();
	// each thread loads one element from global to shared mem
	unsigned int tid = threadIdx.x;
	unsigned int i = blockIdx.x*(blockDim.x*2) + threadIdx.x;//优化位置1
	//printf("tid:%d,i:%d\n",tid, i);
	//sdata[tid] = (i < N) ? g_idata[i] : 0;
	sdata[tid] = (i < N) ? g_idata[i]+ g_idata[i + blockDim.x] : 0;//优化位置2,把第一次归约放在加载数据的时候进行,减少了一次CUDA内部操作
	//printf("CUDA4 i:%d,2i:%d\n", i, i + blockDim.x);
	__syncthreads();//同步共享变量
	switch (GuiYueWay)
	{
	case 4:
		GuiYue_Opt3(sdata, tid);//优化的第3版本的归约函数
		break;
	case 5:
		GuiYue_Opt5(sdata, tid);//优化的第5版本的归约函数
		break;
	default:
		break;
	}
	//GuiYue_Opt3(sdata, tid);//优化的第3版本的归约函数
	if (tid == 0)
	{
		g_odata[blockIdx.x] = sdata[0];
		//printf("g_odata[%d]:%u\n", blockIdx.x,sdata[0]);
	}
	*time = clock() - start;//计时
}



template <unsigned int blockSize>
__global__ void reduce6( unsigned int *g_idata, long long int *g_odata, int N, clock_t* time) //
{//优化6程序
	//__shared__ int sdata[threadsPerBlock];//共享内存,开辟2的指数个空间,2、4、8、32 ...
	//__shared__ unsigned int sdata[threadsPerBlock];//共享内存,开辟2的指数个空间,2、4、8、32 ...
	extern __shared__ long long int sdata[];//动态申请共享内存,空间大小在三个尖括号的第三个参数进行传入<<<1,2,3>>>开辟2的指数个空间,2、4、8、32 ...
	clock_t start = clock();
	// each thread loads one element from global to shared mem
	unsigned int tid = threadIdx.x;
	unsigned int i = blockIdx.x*(blockDim.x * 2) + threadIdx.x;
	//printf("tid:%d,i:%d\n",tid, i);
	//sdata[tid] = (i < N) ? g_idata[i] : 0;
	sdata[tid] = (i < N) ? g_idata[i] + g_idata[i + blockDim.x] : 0;//把第一次归约放在加载数据的时候进行,减少了一次CUDA内部操作

	__syncthreads();//同步共享变量

	//优化的第6版本的归约函数
	if (blockSize >= 512)
	{
		if (tid < 256) { sdata[tid] += sdata[tid + 256]; }__syncthreads();
	}
	if (blockSize >= 256)
	{
		if (tid < 128) { sdata[tid] += sdata[tid + 128]; }__syncthreads();
	}
	if (blockSize >= 128)
	{
		if (tid < 64) { sdata[tid] += sdata[tid + 64]; }__syncthreads();
	}
	if (tid < 32)warpReduceT<blockSize>(sdata, tid);

	if (tid == 0)
	{
		g_odata[blockIdx.x] = sdata[0];
		//printf("sdata[0]:%d\n", sdata[0]);
	}
	*time = clock() - start;//计时
}



template <unsigned int blockSize>
__global__ void reduce7(unsigned int *g_idata,long long int *g_odata, int N, clock_t* time) //
{//优化6程序
	//__shared__ int sdata[threadsPerBlock];//共享内存,开辟2的指数个空间,2、4、8、32 ...
	//__shared__ unsigned int sdata[threadsPerBlock];//共享内存,开辟2的指数个空间,2、4、8、32 ...
	extern __shared__ long long int sdata[];//动态申请共享内存,空间大小在三个尖括号的第三个参数进行传入<<<1,2,3>>>开辟2的指数个空间,2、4、8、32 ...
	clock_t start = clock();
	// each thread loads one element from global to shared mem
	unsigned int tid = threadIdx.x;
	unsigned int i = blockIdx.x*(blockSize * 2) + threadIdx.x;//更改位置1
	unsigned int gridSize = blockSize * 2 * gridDim.x;
	sdata[tid] = 0;
	//printf("tid:%d,i:%d\n",tid, i);
	//sdata[tid] = (i < N) ? g_idata[i] : 0;
	//sdata[tid] = (i < N) ? g_idata[i] + g_idata[i + blockDim.x] : 0;//把第一次归约放在加载数据的时候进行,减少了一次CUDA内部操作
	while (i<N)
	{
		sdata[tid] += g_idata[i] + g_idata[i + blockSize];
		i += gridSize;
	}
	__syncthreads();//同步共享变量

	//优化的第6版本的归约函数
	if (blockSize >= 512)
	{
		if (tid < 256) { sdata[tid] += sdata[tid + 256]; }__syncthreads();
	}
	if (blockSize >= 256)
	{
		if (tid < 128) { sdata[tid] += sdata[tid + 128]; }__syncthreads();
	}
	if (blockSize >= 128)
	{
		if (tid < 64) { sdata[tid] += sdata[tid + 64]; }__syncthreads();
	}
	if (tid < 32)warpReduceT<blockSize>(sdata, tid);//

	if (tid == 0)
	{
		g_odata[blockIdx.x] = sdata[0];
		//printf("sdata[0]:%d\n", sdata[0]);
	}
	*time = clock() - start;//计时
}

int main(int argc, char*argv[])
{
	printf("CUDA测试程序:\n");
	int N = 131072;//总任务数
	int GuiYueWayCPU = 1;//归约的方法,共有7种优化方式
	int threadsPerBlock = 128;//后面执行归约并行计算,该值必须是2的指数值

	printf("argc:%d\n", argc);
	if (argc == 2)
	{
		N = atoi(argv[1]);

	}
	else if (argc == 3)
	{
		N = atoi(argv[1]);
		GuiYueWayCPU = atoi(argv[2]);
	}
	else if (argc >= 4)
	{
		N = atoi(argv[1]);
		GuiYueWayCPU = atoi(argv[2]);
		threadsPerBlock = atoi(argv[3]);
	}

	printf("N:%d\tGuiYueWay:%d\n", N, GuiYueWayCPU);
	int blocksPerGrid = 32;//规约后的长度,重新计算
	
	blocksPerGrid = (N+ threadsPerBlock-1) / threadsPerBlock;//这样可以并发一次搞定
	printf("blocksPerGrid:%d,threadsPerBlock:%d\n", blocksPerGrid, threadsPerBlock);


	unsigned int * inData = new unsigned int[N] {0};
	//int * OutData = new int[N] {0};
	long long int * OutData = new long long int[blocksPerGrid] {0};//输出开辟空间为blocksPerGrid
	clock_t time_used;//CPU 上的
	//long int * inData = new long int[N] {0};
	int * OutData = new int[N] {0};
	//long int * OutData = new long int[blocksPerGrid] {0};//输出开辟空间为blocksPerGrid
	for (int i = 1; i <= N; i++)
	{//赋初始值
		inData[i - 1] = i;
	}
	unsigned int *inData_dev;
	long long int *OutData_dev;//进GPU上的
	clock_t* time;//进GPU上的
	cudaMalloc((void**)&inData_dev, N * sizeof(unsigned int));
	//cudaMalloc((void**)&OutData_dev, N * sizeof(int));
	cudaMalloc((void**)&OutData_dev, blocksPerGrid * sizeof(long long int));
	cudaMalloc((void**)&time, sizeof(clock_t));//开辟空间
	
	cudaMemcpy(inData_dev, inData, N * sizeof(unsigned int), cudaMemcpyHostToDevice);
	//reduce0 << <3, 8 >> > (inData_dev, OutData_dev);
	int smemSize = threadsPerBlock * sizeof(long long int);//动态分配的共享内存大小

	if (GuiYueWayCPU <= 3)
	{//尖括号中第三个参数为动态分配共享内存大小
		reduce0 << <blocksPerGrid, threadsPerBlock, smemSize >> > (inData_dev, OutData_dev, N, time, GuiYueWayCPU);
	}
	else if (GuiYueWayCPU == 4|| GuiYueWayCPU == 5)
	{
		reduce4 << <blocksPerGrid, threadsPerBlock, smemSize >> > (inData_dev, OutData_dev, N, time, GuiYueWayCPU);
	}
	else if (GuiYueWayCPU == 6)
	{
		switch (threadsPerBlock)
		{
		case 512:
			reduce6 <512> << <blocksPerGrid, threadsPerBlock, smemSize >> > (inData_dev, OutData_dev, N, time);
			break;
		case 256:
			reduce6 <256> << <blocksPerGrid, threadsPerBlock, smemSize >> > (inData_dev, OutData_dev, N, time);
			break;
		case 128:
			reduce6 <128> << <blocksPerGrid, threadsPerBlock, smemSize >> > (inData_dev, OutData_dev, N, time);
			break;
		case 64:
			reduce6 <64> << <blocksPerGrid, threadsPerBlock, smemSize >> > (inData_dev, OutData_dev, N, time);
			break;
		case 32:
			reduce6 <32> << <blocksPerGrid, threadsPerBlock, smemSize >> > (inData_dev, OutData_dev, N, time);
			break;
		case 16:
			reduce6 <16> << <blocksPerGrid, threadsPerBlock, smemSize >> > (inData_dev, OutData_dev, N, time);
			break;
		case 8:
			reduce6 <8> << <blocksPerGrid, threadsPerBlock, smemSize >> > ( inData_dev, OutData_dev, N, time);
			break;
		case 4:
			reduce6 <4> << <blocksPerGrid, threadsPerBlock, smemSize >> > ( inData_dev, OutData_dev, N, time);
			break;
		case 2:
			reduce6 <2> << <blocksPerGrid, threadsPerBlock, smemSize >> > (inData_dev, OutData_dev, N, time);
			break;
		case 1:
			reduce6 <1> << <blocksPerGrid, threadsPerBlock, smemSize >> > (inData_dev, OutData_dev, N, time);
			break;
		}
		
	}
	else if (GuiYueWayCPU == 7)
	{
		reduce7 <128> << <blocksPerGrid, threadsPerBlock, smemSize >> > (inData_dev, OutData_dev, N, time);

	}
	else
	{
		printf("GuiYueWayCPU:%d define error!\n", GuiYueWayCPU);
		return -1;
	}

	cudaMemcpy(OutData, OutData_dev, blocksPerGrid * sizeof(long long int), cudaMemcpyDeviceToHost);
	
	cudaMemcpy(&time_used, time, sizeof(clock_t), cudaMemcpyDeviceToHost);
	long long int sum = 0;
	for (int i = 0; i < blocksPerGrid; i++)
	{
		//printf("%d=>%u\n", i, OutData[i]);
		sum += OutData[i];
	}

	printf("N:%d,result sum:%lld,GuiYueWay:%d,time: %d\n", N,sum, GuiYueWayCPU,time_used);


	//释放GPU上的内存
	cudaFree(inData_dev);
	cudaFree(OutData_dev);
	cudaFree(time);


	//释放CPU上的内存
	delete[]inData;
	delete[]OutData;

	return 0;

}

//*/


推荐阅读