CUDA 计算pi (π)
通过简单的程序设计熟练CUDA的使用步骤
下面是cuda代码及相关注释
#include <stdio.h>
#include <iostream>
#include <time.h>
//#include <cutil_inline.h>
using namespace std; //*****************************************//
//以下两部分将在设备上编译 由__global__标识;
template<typename T> __global__ void reducePI1(T* __restrict__ d_sum, int num){
//__restrict__ 是说从只读缓存中读取该数据,会有什么优势呢?
//printf("blockIdx.x is %d\n",blockIdx.x);//线程块索引,0~grid-1
//printf("blockDim.x is %d\n",blockDim.x);//线程块包含的线程数,这里就是<<<grid,block,size>>>中的block
//printf("threadIdx.x is %d\n",threadIdx.x);//每个线程块中线程的标号,0~block-1
int id = blockIdx.x*blockDim.x + threadIdx.x;//为每个线程构建唯一标号,0~grid*block-1 T temp;
T pSum = ;
extern T __shared__ s_pi[];//数据存放在共享存储上,只有本线程块内的线程可以访问
T rnum = 1.0/num; for(int i=id;i<num;i +=blockDim.x*gridDim.x){
//每个线程计算的次数是总的次数(num)除以总的线程数(grid*block)
temp = (i+0.5f)*rnum;
pSum += 4.0f/(+temp*temp);
} s_pi[threadIdx.x] = pSum*rnum;//每个线程块中的线程会把自己计算得到的s_pi独立存储在本块的共享存储上
__syncthreads();//等待本块所有线程计算完毕 for(int i = (blockDim.x>>);i >;i >>= ){
//将本块内的 计算结果 进行累加
if (threadIdx.x<i){
s_pi[threadIdx.x] += s_pi[threadIdx.x+i];
}
__syncthreads();
}
//将加和的结果写到本块对应的显存中,以备reducePI2使用
if (threadIdx.x==)
{
d_sum[blockIdx.x]=s_pi[];
} //下面这段代码应该是在执行类似的算法但是结果会有很大偏差,并未找到原因^_^
//if (warpSize>63){
// if (threadIdx.x<32){
// s_pi[threadIdx.x] += s_pi[threadIdx.x +32];
// }
//}
//if (threadIdx.x<16){
// s_pi[threadIdx.x] += s_pi[threadIdx.x +16];
//printf("threadIdx.x 16 is %d\n",threadIdx.x);
//}
//if (threadIdx.x<8){
// s_pi[threadIdx.x] += s_pi[threadIdx.x +8];
//printf("threadIdx.x 8 is %d\n",threadIdx.x);
//}
//if (threadIdx.x<4){
// s_pi[threadIdx.x] += s_pi[threadIdx.x +4];
//printf("threadIdx.x 4 is %d\n",threadIdx.x);
//}
//if (threadIdx.x<2){
// s_pi[threadIdx.x] += s_pi[threadIdx.x +2];
//printf("threadIdx.x 2 is %d\n",threadIdx.x);
//}
//if (threadIdx.x<1){
// d_sum[blockIdx.x] = s_pi[0]+s_pi[1];
//printf("threadIdx.x 1 is %d\n",threadIdx.x);
//} } template<typename T> __global__ void reducePI2(T* __restrict__ d_sum, int num, T* __restrict__ d_pi){
int id = threadIdx.x;//这个函数的线程块只有一个,线程数是grid,这里依然用id作为索引名
extern T __shared__ s_sum[];//这个是共享内存中的,只有块内可见
s_sum[id]=d_sum[id];//把显存中的数据装载进来
__syncthreads();//等待装载完成 for(int i = (blockDim.x>>);i>;i >>=)
//仍然采用半对半折和的方法对本块内所有线程中的s_sum进行求和
{
if (id<i){
s_sum[id] += s_sum[id+i];
}
__syncthreads();//等待求和完成
}
//将求和结果写入显存,使得cpu主机端可见
if(threadIdx.x==)
{
*d_pi =s_sum[];
}
//if (warpSize>63){
// if (threadIdx.x<32){
// s_sum[threadIdx.x] += s_sum[threadIdx.x +32];
// }
//}
//if (threadIdx.x<16){
// s_sum[threadIdx.x] += s_sum[threadIdx.x +16];
//}//
//if (threadIdx.x<8){
// s_sum[threadIdx.x] += s_sum[threadIdx.x +8];
//}
//if (threadIdx.x<4){
// s_sum[threadIdx.x] += s_sum[threadIdx.x +4];
//}
//if (threadIdx.x<2){
// s_sum[threadIdx.x] += s_sum[threadIdx.x +2];
//}
//if (threadIdx.x<1){
// *d_pi = s_sum[0]+s_sum[1];
//} } //**********************************************//
//以下代码在主机上编译 template <typename T> T reducePI(int num){ int grid = ;//用来调整线程块的数量 T *tmp;
cudaMalloc((void**)&tmp,grid*sizeof(T));//在设备存储器(显存)上开辟grid*sizeof(T)大小的空间,主机上的指针tmp指向该空间
reducePI1<<<grid,,*sizeof(T)>>>(tmp,num);//调用reducePI1
//参数表示有grid个线程块,每个线程块有256个线程,每个线程块使用256*size大小的共享存储器(只有块内可以访问) //执行之后,会在tmp为首的显存中存储grid 个中间结果
//printf("%d\n",__LINE__);//显示代码所在行号,不知会有什么用
T *d_PI;
cudaMalloc((void**)&d_PI,sizeof(T));//显存中为π的计算结果开辟空间 reducePI2<<<,grid,grid*sizeof(T)>>>(tmp,grid,d_PI);//只有一个线程块,有grid个线程
//执行后在显存中d_PI的位置存放最后结果
T pi;//这是在主机内存上的空间
cudaMemcpy(&pi,d_PI,sizeof(T),cudaMemcpyDeviceToHost);//从显存中将数据拷贝出来
cudaFree(tmp);//释放相应的显存空间
cudaFree(d_PI); return pi;
} template <typename T> T cpuPI(int num){ T sum = 0.0f;
T temp;
for (int i=;i<num;i++)
{
temp =(i+0.5f)/num;
sum += /(+temp*temp);
}
return sum/num; } int main(){
printf("test for compell \n");
clock_t start, finish;//用来计时
float costtime;
start = clock();
//************
printf("cpu pi is %f\n",cpuPI<float>());//调用普通的串行循环计算 π
//*************
finish = clock();
costtime = (float)(finish - start) / CLOCKS_PER_SEC; //单位是秒
printf("costtime of CPU is %f\n",costtime); start = clock();
//************
printf("gpu pi is %f\n",reducePI<float>());//调用主机上的并行计算函数
//************
finish = clock();
costtime = (float)(finish - start) / CLOCKS_PER_SEC;
printf("costtime of GPU is %f\n",costtime);
return ;
}
编译和执行
nvcc computePIsave.cu -I /usr/local/cuda-8.0/include -L /usr/local/cuda-8.0/lib64 -o test
./test
当设定num数量少时cpu的计算耗时会比gpu短,但是随着num的增加,cpu的耗时会成比例增加,但是gpu耗时基本没有变化。
CUDA 计算pi (π)的更多相关文章
- cuda计算的分块
gpu的架构分为streaming multiprocessors 每个streaming multiprocessors(SM)又能分步骤执行很多threads,单个SM内部能同时执行的thread ...
- 计算pi的精度+进度条显示
步骤1:安装tqdm 首先,要打开cmd,输入指令,不断找到python文件的路径,知道找到Scripts,然后分别打入pip install pygame和pip install tqdm 如下图 ...
- 概率法计算PI
#include <iostream> using namespace std; //概率计算PI int main() { ; double val; int i; ; i<; i ...
- 一个很牛的计算pi的c程序!
C语言是面向过程的一种高级程序设计语言,它在世界范围内使用很广泛,而且很流行.很多大型的应用软件,基本上是用C语言所编写的.在对操作系统以及系统使用程序.需要对硬件进行操作的场合,C语言较其他的高级语 ...
- LINUX上一个命令计算PI
Linux上一个命令计算PI – 笑遍世界 http://smilejay.com/2017/11/calculate-pi-with-linux-command/ [root@d1 goEcho]# ...
- CUDA 计算线程索引的一般公式
CUDA thread index: int blockId = blockIdx.z * (gridDim.x*gridDim.y) + blockIdx.y ...
- 用随机投掷飞镖法计算Pi值(Randomness Throwing dart Pi Python)
画一个边长为r的正方形和半径为r的四分之一的圆(如下图所示),向上面随机投掷飞镖,通过计算落在星星区域和整体区域的飞镖比例,即可求出π值. 公式推导如下: 假设正方形的边长r为1,那么飞镖落在星星区域 ...
- 算法之美--1.蒙特卡洛方法计算pi
基本思想: 利用圆与其外接正方形面积之比为pi/4的关系,通过产生大量均匀分布的二维点,计算落在单位圆和单位正方形的数量之比再乘以4便得到pi的近似值.样本点越多,计算出的数据将会越接近真识的pi(前 ...
- CUDA计算矩阵相乘
1.最简单的 kernel 函数 __global__ void MatrixMulKernel( float* Md, float* Nd, float* Pd, int Width) { int ...
随机推荐
- 【萌新向】cartographer_ros最新安装指南 2019-12
首先说明,这篇文章于2019年12月写下,cartographer现在还在不断更新,也许这篇文档内容会失效,但是大体是一样的安装流程. 一.硬件条件 使用平台 x86_64平台 ROS发行版 Indi ...
- 利用docker容器运行.net core webapi
利用docker容器运行.net core webapi :first-child { margin-top: 0 !important; } > :last-child { margin-bo ...
- javaweb-实现不同用户登录后跳转到不同界面
今天下午,实现了公文流转系统的一小部分——登录界面验证不同用户跳转到不同界面 Dao package com.office.Dao; import java.sql.Connection; impor ...
- lombok优缺点
优点: 能通过注解的形式自动生成构造器.getter/setter.equals.hashcode.toString等方法,提高了一定的开发效率 让代码变得简洁,不用过多的去关注相应的方法 属性做修改 ...
- Appium+Pytest实现app并发测试
前言 这个功能已经写完很长时间了,一直没有发出来,今天先把代码发出来吧,有一些代码是参考网上写的,具体的代码说明今天暂时先不发了,代码解释的太详细还得我花点时间^_^, 毕竟想让每个人都能看明白也不容 ...
- 常见的java异常——java.lang.IllegalStateException: Ambiguous handler methods mapped for HTTP path
此异常是由于你的controller中有两个名字与内容相同的方法: 出现此异常时去检查你的controller中是否有重复的名字的方法:
- 日志管理-log4j与slf4j的使用
一.概述 1.log4j: Log4j是Apache的一个开源项目,通过使用Log4j,我们可以控制日志信息输送的目的地是控制台.文件.GUI组件,甚至是套接口服务器.NT的事件记录器.UNIX Sy ...
- $Noip2016/Luogu2827$蚯蚓
$Luogu$ $Sol$ 乍一看就是个模拟叭,用个优先队列维护不就好了.不过这里有一个问题就是怎么解决没被切的蚯蚓的增长问题.可以这样处理,每次切一条蚯蚓,给切完之后的都减去$q$,最后输出答案时都 ...
- C / C++ 保留小数函数(setprecision(n)的一些用法总结)
从C语言开始正式学习C++,但是一上来输出位数就懵了,查资料才知道C++需要使用 “ setprecision ”函数.自己总结一下. 首先说C++代码 #include <iomanip&g ...
- 处理样本不平衡的LOSS—Focal Loss
0 前言 Focal Loss是为了处理样本不平衡问题而提出的,经时间验证,在多种任务上,效果还是不错的.在理解Focal Loss前,需要先深刻理一下交叉熵损失,和带权重的交叉熵损失.然后我们从样本 ...