GPU 编程入门到精通(三)之 第一个 GPU 程序
博主因为工作其中的须要。開始学习 GPU 上面的编程,主要涉及到的是基于 GPU 的深度学习方面的知识,鉴于之前没有接触过 GPU 编程,因此在这里特地学习一下 GPU 上面的编程。有志同道合的小伙伴。欢迎一起交流和学习,我的邮箱:
caijinping220@gmail.com 。使用的是自己的老古董笔记本上面的 Geforce 103m 显卡,尽管显卡相对于如今主流的系列已经很的弱,可是对于学习来说,还是能够用的。本系列博文也遵从由简单到复杂,记录自己学习的过程。
0. 文件夹
- GPU 编程入门到精通(一)之 CUDA 环境安装
- GPU 编程入门到精通(二)之 执行第一个程序
- GPU 编程入门到精通(三)之 第一个 GPU 程序
- GPU 编程入门到精通(四)之 GPU 程序优化
- GPU 编程入门到精通(五)之 GPU 程序优化进阶
1. 数组平方和并行化
GPU 编程入门到精通(三)之 第一个 GPU 程序 中讲到了怎样利用
CUDA5.5 在 GPU 中执行一个程序。通过程序的执行,我们看到了 GPU 确实能够作为一个运算器,可是。我们在前面的样例中并没有正真的发挥 GPU 并行处理程序的能力,也就是说之前的样例仅仅利用了 GPU 的一个线程。没有发挥程序的并行性。
先来说说 CUDA5.5 中 GPU 的架构。它是由 grid 组成。每一个 grid 又能够由 block 组成,而每一个 block 又能够细分为 thread。所以,线程是我们处理的最小的单元了。
接下来的样例通过改动前一个样例,把数组切割成若干个组(每一个组由一个线程实现)。每一个组计算出一个和,然后在 CPU 中将分组的这几个和加在一起,得到终于的结果。这样的思想叫做归约 。事实上和分治思想差点儿相同,就是先将大规模问题分解为小规模的问题。最后这些小规模问题整合得到终于解。
因为我的 GPU 支持的块内最大的线程数是 512 个,即 中的
cudaGetDeviceProperties 属性。
maxThreadsPerBlock
怎样获取这个属性,请參看 GPU 编程入门到精通(二)之 执行第一个程序 这一章节。
我们使用 512 个线程来实现并行加速。
好了,接下来就是敲代码的时候了。
1.1. 改动代码
首先,在程序头部添加一个关于线程数量的宏定义:
// ======== define area ========
#define DATA_SIZE 1048576 // 1M
#define THREAD_NUM 512 // thread num
当中,DATA_SIZE 表示处理数据个数, THREAD_NUM 表示我们将要使用 512 个线程。
其次,改动 GPU 部分的内核函数
const int size = DATA_SIZE / THREAD_NUM;
const int tid = threadIdx.x;
int tmp_sum = 0; for (int i = tid * size; i < (tid + 1) * size; i++) {
tmp_sum += data[i] * data[i];
}
sum[tid] = tmp_sum;
}
此内核程序的目的是把输入的数据分摊到 512 个线程上去计算部分和,而且 512 个部分和存放到 sum 数组中。最后在 CPU 中对 512 个部分和求和得到终于结果。
此处对数据的遍历方式请注意一下。我们是依据顺序给每个线程的。也就是例如以下表格所看到的:
线程编号 数据下标 0 0 ~ 2047 … … … … 511 1046528 ~ 1048575 然后,改动主函数部分
主函数部分,仅仅须要把 sum 改成数组就能够,而且设置一下调用 GPU 内核函数的方式。// malloc space for datas in GPU
cudaMalloc((void**) &sum, sizeof(int) * THREAD_NUM); // calculate the squares's sum
squaresSum<<<1, THREAD_NUM, 0>>>(gpuData, sum, time);
最后,在 CPU 内添加部分和求和的代码
// print result
int tmp_result = 0;
for (int i = 0; i < THREAD_NUM; ++i) {
tmp_result += result[i];
}
printf("(GPU) sum:%d time:%ld\n", tmp_result, time_used);
1.2. 编译执行
编译后。执行结果例如以下所看到的:
2. 性能分析
经过改动以后的程序。比之前的快了将近 36 倍(能够參考博文
GPU 编程入门到精通(三)之 第一个 GPU 程序 进行比較),可见并行化处理还是存在优势的。 只是细致想一下,我们使用了 512 个线程, 但是性能怎么才提升了 36 倍。不应该是 512 倍吗???
这里就涉及到内存的存取模式了。显卡上面的内存是 DRAM。是效率最高的存取方式,它是一种连续的存取方式。 前面我们的程序确实的连续读取的呀,都挨个读取了,怎么还是没有达到预期的效果呢???
这里还须要考虑 thread 的运行方式,GPU 编程入门到精通(三)之 第一个 GPU 程序 中说到,当一个
thread 在等待内存数据的时候, GPU 就会切换到下一个 thread。所以,实际运行的顺序类似于 thread0 —> thread1 —> … … —> thread511。
这就导致了同一个 thread 在读取内存是连续的。 可是对于总体而言,运行的过程中读取就不是连续的了(这里自己细致想想,就明确了)。所以,正确的做法例如以下表格所看到的:
| 线程编号 | 数据下标 |
|---|---|
| 0 | 0 ~ 512 |
| … … | … … |
| 511 | 511 ~ 1023 |
依据这个原理,改动内核函数例如以下:
for (int i = tid; i < DATA_SIZE; i += THREAD_NUM) {
tmp_sum += data[i] * data[i];
}
编译执行后结果例如以下所看到的:
watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQveHNjX2M=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="thread512_plus" style="max-width:100%">
改动后程序,比之前的又快了 13 倍左右。可见,对内存的读取方式对于性能的影响非常大。
至此,并行化后的程序较未并行化之前的程序,速度上快了 493 倍左右,可见,基本上发挥了 512 个线程的优势。
让我们再来分析一下性能:
此 GPU 消耗的时钟周期: 1595788 cycles
GeForce G 103M 的 clockRate: 1.6 GHz
所以能够计算出 GPU 上执行时间是: 时钟周期 / clockRate = 997.3675 us
1 M 个 int 型数据有 4M Byte 的数据量,实际使用的 GPU 内存带宽是:数据量 / 执行时间 = 4.01 GB/s
再来看看我的 GPU GeForce 103m 的内存带宽:执行 SDK 文件夹以下 /samples/1_Utilities/bandwidthTest
执行后结果例如以下所看到的:
通过与系统參数的对照,能够知道,基本上达到了系统的极限性能。
这一篇博文介绍了怎样通过利用线程达到程序的并行计算,而且通过优化内存读取方式,实现对程序的优化。通过这个程序,能够学会使用 CUDA 线程的一般流程。下一部分,将进一步分析程序可优化的一些细节。
欢迎大家和我一起讨论和学习 GPU 编程。
GPU 编程入门到精通(三)之 第一个 GPU 程序的更多相关文章
- GPU 编程入门到精通(五)之 GPU 程序优化进阶
博主因为工作其中的须要,開始学习 GPU 上面的编程,主要涉及到的是基于 GPU 的深度学习方面的知识.鉴于之前没有接触过 GPU 编程.因此在这里特地学习一下 GPU 上面的编程. 有志同道合的小伙 ...
- GPU 编程入门到精通(四)之 GPU 程序优化
博主因为工作其中的须要,開始学习 GPU 上面的编程,主要涉及到的是基于 GPU 的深度学习方面的知识,鉴于之前没有接触过 GPU 编程.因此在这里特地学习一下 GPU 上面的编程.有志同道合的小伙伴 ...
- iOS开发-UI 从入门到精通(三)
iOS开发-UI 从入门到精通(三)是对 iOS开发-UI 从入门到精通(一)知识点的综合练习,搭建一个简单地登陆界面,增强实战经验,为以后做开发打下坚实的基础! ※在这里我们还要强调一下,开发环境和 ...
- 微博,and java 多线程编程 入门到精通 将cpu 的那个 张振华
http://down.51cto.com/data/2263476 java 多线程编程 入门到精通 将cpu 的那个 张振华 多个用户可以同时用一个 vhost,但是vhost之间是隔离的. ...
- WPF MVVM从入门到精通2:实现一个登录窗口
原文:WPF MVVM从入门到精通2:实现一个登录窗口 WPF MVVM从入门到精通1:MVVM模式简介 WPF MVVM从入门到精通2:实现一个登录窗口 WPF MVVM从入门到精通3:数据绑定 ...
- Windows Azure入门教学系列 (一): 创建第一个WebRole程序
原文 Windows Azure入门教学系列 (一): 创建第一个WebRole程序 在第一篇教学中,我们将学习如何在Visual Studio 2008 SP1中创建一个WebRole程序(C#语言 ...
- Cesium入门2 - Cesium环境搭建及第一个示例程序
Cesium入门2 - Cesium环境搭建及第一个示例程序 Cesium中文网:http://cesiumcn.org/ | 国内快速访问:http://cesium.coinidea.com/ 验 ...
- MyBatis从入门到精通(三):MyBatis XML方式的基本用法之多表查询
最近在读刘增辉老师所著的<MyBatis从入门到精通>一书,很有收获,于是将自己学习的过程以博客形式输出,如有错误,欢迎指正,如帮助到你,不胜荣幸! 1. 多表查询 上篇博客中,我们示例的 ...
- cg语言学习&&阳春白雪GPU编程入门学习
虽然所知甚少,但康大的<GPU编程与Cg编程之阳春白雪下里巴人>确实带我入了shader的门,在里面我第一次清晰地知道了“语义”的意思,非常感谢. 入门shader,我觉得可以先读3本书: ...
随机推荐
- Java编程思想读书笔记_第6章
如何创建一个包: 比如创建一个包名为com.huawei 则在一个目录下如(d:\java)创建目录com/huawei 然后在huawei目录下创建一个文件Assist.java package c ...
- 第四次团队作业——项目Alpha版本发布
这个作业属于哪个课程 <课程的链接> 这个作业要求在哪里 <作业要求的链接> 团队名称 Three cobblers 这个作业的目标 发布项目α版本,对项目进 ...
- Codewars练习Python
计算一个数组的中间数,数的两边和相等,并返回index值 如:数组[1,2,3,4,6] 返回3(数组序号从0开始) def find_even_index(arr): ""&qu ...
- Spring注解的步骤
Spring框架提供DI(属性注解)和IOC(类/Bean的注解)注解. 注解:标注.注入和解析.解释;标注和解释一部分代码的作用在框架中:就是配置文件的另外一种实现方式@Type.@Taget;减少 ...
- miller_rabin_素性测试
摘自:http://blog.csdn.net/pi9nc/article/details/27209455 看了好久没看懂,最后在这篇博客中看明白了. 费马定理的应用,加上二次探测定理. Ferma ...
- js获取微信版本号的方法
微信现在几乎已经普遍在使用,但是微信版本一直在更新,不同的版本有不同的功能,于是了解微信的版本号很重要,以便自己及时更新体验新的功能.那么如何用js获取微信版本号呢? 一.判断微信版本号 ? 1 2 ...
- HDU6189 Law of Commutation (数论)
题意:输入n和a 定义m等于2的n次方 求1-m有多少数使得 a^b = b^a (mod m) 题解:先打表找规律 发现a为奇数的答案只有b = a这一种 (不知道为什么也不想知道为什么 当a为偶数 ...
- Python常用的内建模块
PS:Python之所以自称“batteries included”,就是因为内置了许多非常有用的模块,无需额外安装和配置,即可直接使用.下面就来看看一些常用的内建模块. 参考原文 廖雪峰常用的内建模 ...
- NOIP2016 DAY1 T2天天爱跑步
传送门 题目描述 小c同学认为跑步非常有趣,于是决定制作一款叫做<天天爱跑步>的游戏.«天天爱跑步»是一个养成类游戏,需要玩家每天按时上线,完成打卡任务. 这个游戏的地图可以看作一一棵包含 ...
- SIMD学习 -- 用SSE2指令作点乘和累加计算
这几天在做学校的一个学习小项目,需要用到SIMD指令计算提速.也是第一次碰这个,看了一些资料和代码,模仿着写了两个函数. void sse_mul_float(float *A, float *B, ...