本章目标
- 了解NVIDIA GPU计算功能集
- 了解原子操作以及为什么需要它们
- 了解如何在CUDA C中执行带有原子操作的运算
NVIDIA GPU计算功能集
功能集为1.2既支持共享内存原子操作又支持全局内存原子操作,功能集向下兼容,由于本书较老,目前功能集版本可查看最新设备列表。
基于最小功能集的编译
可以指定只有在1.1或更高版本的计算功能集中才支持的编译优化。在编译时,只需增加命令选项:
nvcc -arch=sm_11
原子操作简介
将程序由单线程改为多线程时,如果多个线程需要对共享值进行读取或者写入,那么结果很有可能产生错误。 例如x++操作,此操作包括如下三个步骤:
- 读取X的值
- 将读取的值加1
- 将递增结果写到X 现在有线程A和线程B都对X进行递增,理想情况如果X为7,则最终结果为9。 但是两个线程有可能交叉进行,造成错误。 在上述示例中,我们需要通过某种方式一次性执行完读取-修改-写入的操作,并且在执行过程中不被其它线程中断。具体来讲,除非已经完成了这三个操作,否则其他线程都不能读取或者写入X的值。由于这些操作的执行过程不能分解为更小的部分,因此我们将满足这种条件限制的操作称为原子操作。CUDA C支持多种原子操作。
示例程序中包括了CPU版与1 .使用全局内存原子操作的直方图核函数 2.使用共享内存原子和全局内存原子操作的直方图核函数
1中与CPU性能对比时,可能会发现性能可能更糟。由于核函数只包含了非常少量的工作,很可能是全局内存上的原子操作导致了性能的降低。当数千个线程尝试访问少量的内存位置时,将导致大量竞争。为了确保递增操作的原子性,对相同位置的操作都将被硬件串行化。所以我们需要改进算法。
2中在第一个阶段,每个并行线程块将计算它所处理数据的直方图。由于每个线程块在执行这个操作时是相互独立的,因此可以在共享内存中计算这些直方图。由于现在只有256个线程在256个地址上发生竞争,这将极大的减少在使用全局内存时在数千个线程之间发生竞争的情况。
示例程序请看https://github.com/mengzhangjian/cuda-learning-example 中CH09。