GPU 编程入门到精通(三)之 第一个 GPU 程序

2018-01-10  本文已影响846人  04282aba96e3

0. 目录

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. 修改代码

1.2. 编译运行

编译后,运行结果如下所示:

[图片上传失败...(image-cd7585-1515573581453)]

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];
}

编译运行后结果如下所示:

thread512_plus

修改后程序,比之前的又快了 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

运行后结果如下所示:

bandwidth

通过与系统参数的对比,可以知道,基本上达到了系统的极限性能。


<center style="box-sizing: border-box; color: rgb(69, 69, 69); font-family: "PingFang SC", "Microsoft YaHei", SimHei, Arial, SimSun; font-size: 16px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); text-decoration-style: initial; text-decoration-color: initial;">
这一篇博文介绍了如何通过利用线程达到程序的并行计算,并且通过优化内存读取方式,实现对程序的优化。通过这个程序,可以学会使用 CUDA 线程的一般流程。下一部分,将进一步分析程序可优化的一些细节。</center>

上一篇下一篇

猜你喜欢

热点阅读