【CUDA并行编程之七】数组元素之和

现在需要求得一个数组的所有元素之和,之前感觉似乎不太可能,因为每个线程只处理一个元素,无法将所有元素联系起来,但是最近学习了一段代码可以实现,同时也对shared memory有了进一步的理解。

一、C++串行实现

串行实现的方法非常之简单,只要将所有元素依次相加就能够得到相应的结果,实际上我们注重的不是结果,而是运行的效率。那么代码如下:

array_sum.cc:

#include<iostream>
#include<stdio.h>
#include "kmeans.h"

using namespace std;

const int cnt = 100000;

int main()
{
	int *a = new int[cnt];

	for(int i=0;i<cnt;i++)
	{
		a[i] = i+1;
	}

	double t = wtime();

	for(int i=0;i<cnt;i++)
	 	sum += a[i];

	printf("computation elapsed %.8f \n",wtime()-t);

	return 0;
}

wtime.cu:

#include <sys/time.h>
#include <stdio.h>
#include <stdlib.h>

double wtime(void)
{
    double          now_time;
    struct timeval  etstart;
    struct timezone tzp;

    if (gettimeofday(&etstart, &tzp) == -1)
        perror("Error: calling gettimeofday() not successful.\n");

    now_time = ((double)etstart.tv_sec) +              /* in seconds */
               ((double)etstart.tv_usec) / 1000000.0;  /* in microseconds */
    return now_time;
}

运行结果:

二、CUDA并行实现

先上代码然后再进行解释:

#include <iostream>
#include <stdio.h>
#include "kmeans.h"

using namespace std;

const int count = 1000;

void generate_data(int *arr)
{
	for(int i=0;i<count;i++)
	{
		arr[i] = i+1;
	}
}

int nextPowerOfTwo(int n)
{
	n--;
	n = n >> 1 | n;
	n = n >> 2 | n;
	n = n >> 4 | n;
	n = n >> 8 | n;
	n = n >> 16 | n;
	//n = n >> 32 | n; //For 64-bits int
	return ++n;
}

/*
cnt : count
cnt2 : next power of two of count
*/
__global__ static void compute_sum(int *array,int cnt , int cnt2)
{
	extern __shared__ unsigned int sharedMem[];
	sharedMem[threadIdx.x] = (threadIdx.x < cnt) ? array[threadIdx.x] : 0 ;
	__syncthreads();

	//cnt2 "must" be a power of two!
	for( unsigned int s = cnt2/2 ; s > 0 ; s>>=1 )
	{
		if( threadIdx.x < s )
		{
			sharedMem[threadIdx.x] += sharedMem[threadIdx.x + s];
		}
		__syncthreads();
	}
	if(threadIdx.x == 0)
	{
		array[0] = sharedMem[0];
	}
}

int main()
{
	int *a = new int[count];
	generate_data(a);

	int *deviceArray;
	cudaMalloc( &deviceArray,count*sizeof(int) );
	cudaMemcpy( deviceArray,a,count*sizeof(int),cudaMemcpyHostToDevice );
	int npt_count = nextPowerOfTwo(count);//next power of two of count
	//cout<<"npt_count = "<<npt_count<<endl;
	int blockSharedDataSize = npt_count * sizeof(int);

	double t = wtime();
	for(int i=0;i<count;i++)
	{
		compute_sum<<<1,count,blockSharedDataSize>>>(deviceArray,count,npt_count);
	}
	printf("computation elapsed %.8f \n",wtime()-t);

	int sum ;
	cudaMemcpy( &sum,deviceArray,sizeof(int),cudaMemcpyDeviceToHost );
	cout<<"sum = "<<sum<<endl;

	return 0;
}

主函数:

line58:为数组a赋初值,维度为count。

line60~62:定义device变量并分配内存,将数组a的值拷贝到显存上去。

line63:nextPowerOfTwo是非常精妙的一段代码,它计算大于等于输入参数n的第一个2的幂次数。至于为什么这么做要到kernel函数里面才能明白。

line68:compute_sum中的"1"为block的数量,"count"为每个block里面的线程数,"blockSharedDataSize"为共享内存的大小。

核函数compute_sum:

line35:定义shared memory变量。

line36:将threadIdx.x小于cnt的对应的sharedMem的内存区赋值为数组array中的值。

line39~47:这段代码的功能就是将所有值求和之后放到了shareMem[0]这个位置上。这段代码要好好体会一下,它把原本计算复杂度为O(n)的串行实现的时间效率通过并行达到了O(logn)。最后将结果保存到array[0]并拷贝回主存。

makefile:

cu:
	nvcc cuda_array_sum.cu wtime.cu
	./a.out

结果:

三、效率对比

我们通过修改count的值并且加大循环次数来观察变量的效率的差别。

代码修改:

运行时间对比:


count
串行(s)
并行(s)

1000

0.00627995

0.00345612

10000

0.29315591

0.06507015

100000

25.18921304

0.65188980

1000000

2507.66827798

5.61648989

哈哈,可见在数据量大的情况下效率还是相当不错的。

Author:忆之独秀

Email:[email protected]

注明出处:http://blog.csdn.net/lavorange/article/details/43031419

时间: 2024-10-09 21:13:11

【CUDA并行编程之七】数组元素之和的相关文章

【CUDA并行编程之三】Cuda矢量求和运算

本文将通过矢量求和运算来说明基本的Cuda并行编程的基本概念.所谓矢量求和运算,就是两个数组数据中对应的元素两两相加,并将结果保存在第三个数组中.如下图所示: 1.基于CPU的矢量求和: 代码非常简单: #include<iostream> using namespace std; const int N =10; void add( int *a ,int *b , int *c) { int tid = 0; while(tid < N) { c[tid] = a[tid] + b[

CUDA 并行编程简介

前言 并行就是让计算中相同或不同阶段的各个处理同时进行.目前有很多种实现并行的手段,如多核处理器,分布式系统等.本专题的文章将主要介绍使用 GPU 实现并行的方法.参考本专题文章前请务必搭建好 CUDA 开发平台,搭建方法可以参考上一篇文章. GPU 并行的优缺点 优点: 1. 显存具有更大的内存带宽 2. GPU 具有更大量的执行单元 3. 价格低廉 缺点: 1. 对于不能高度并行化的工作,能带来帮助不大. 2. 对于绝大多数显卡型号,CUDA 仅支持 float 类型而不支持 double

【CUDA并行编程之五】计算向量的欧式距离

本文将介绍如何用cuda来计算两个向量之间的欧式距离,其中涉及到了如果将二维矩阵传入到核函数进行计算的问题,并且介绍两个内存分配和拷贝的API:cudaMallocPitch以及cudaMemcpy2D. 一.需求分析 现在我们要解决这么一个问题:计算一个D维的向量A[D]到二维矩阵B[N][D]的每一行的欧式距离,并且将每一组距离保存在一个向量dis[N]中并返回.我们还是通过串行和并行两种方式来进行实现. 二.串行实现 实现方法就是用一个二重循环进行相乘,然后将结果保存.上代码: dis_c

【CUDA并行编程之八】Cuda实现Kmeans算法

本文主要介绍如何使用CUDA并行计算框架编程实现机器学习中的Kmeans算法,Kmeans算法的详细介绍在这里,本文重点在并行实现的过程. 当然还是简单的回顾一下kmeans算法的串行过程: 伪代码: 创建k个点作为起始质心(经常是随机选择) 当任意一个点的簇分配结果发生改变时 对数据集中的每个数据点 对每个质心 计算质心与数据点之间的距离 将数据点分配到距其最近的簇 对每一个簇,计算簇中所有点的均值并将均值作为质心 我们可以观察到有两个部分可以并行优化: ①line03-04:将每个数据点到多

【CUDA并行编程之四】矩阵相乘

前面介绍了基本的Cuda编程的相关知识,那么这一篇在此基础之上来看看GPU在处理数据计算上的高效能,我们拿矩阵相乘来作为例子. 1.CPU上执行矩阵相乘以及性能. 在CPU上进行矩阵相乘运算的代码: mat_mul.cc: <span style="font-family:Microsoft YaHei;font-size:18px;">//a[i]*b[i] + c[i] = d[i] #include<iostream> #include<vector

【Cuda并行编程之二】Cuda Memory Hierarchy_Cuda内存层次结构

要想编写高效的程序,那么一定要对内存结构有比较深刻的认识,就像C/C++里面的堆内存,栈内存,全局存储区,静态存储区,常量区等.Cuda是并行计算框架,而GPU的内存有限,那么如果想编写高效的Cuda程序,首先要对其内存结构有一个简单的认识. 首先我们先上一张图,然后通过解释一些名词和代码来进行解释. 各种存储器比较: 存储器  位置 拥有缓存 访问权限 变量生存周期 register GPU片内 N/A device可读/写 与thread相同 local memory 板载显存 无 devi

【CUDA并行编程之六】KNN算法的并行实现

之前写了两篇文章一个是KNN算法的C++串行实现,另一个是CUDA计算向量的欧氏距离.那么这篇文章就可以说是前两篇文章的一个简单的整合.在看这篇文章之前可以先阅读前两篇文章. 一.生成数据集 现在需要生成一个N个D维的数据,没在一组数据都有一个类标,这个类标根据第一维的正负来进行标识样本数据的类标:Positive and Negative. #!/usr/bin/python import re import sys import random import os filename = "in

CUDA并行编程思维过程

1)确定应用程序中需要且可以并行化的部分 2)将并行化代码中需要用到的数据分离出来,具体方法是用API函数在并行技术设备上分配内存空间 3)用API函数将数据传输到并行计算设备上 4)在并行化部分开发一个kernel函数,该函数由其中个别线程执行 5)并行线程执行且启动kernel函数 6)最后调用API函数将数据传回主机处理器

cuda并行编程之求解ConjugateGradient(共轭梯度迭代)丢失dll解决方案

在进行图像处理过程中,我们经常会用到梯度迭代求解大型现在方程组:今天在对奇异矩阵进行求解的时候,出现了缺少dll的情况: 报错如下图: 缺少cusparse32_60.dll 缺失cublas32_60.dll 解决方案: (1)将cusparse32_60.dll和cublas32_60.dll直接拷贝到C:\Windows目录,但这样在一直的时候,还会出现同样错误,为了避免麻烦,最好采用方法(2) (2)将cusparse32_60.dll和cublas32_60.dll拷贝到你所在项目的文