到目前为止,我们已经学会了如何编写CUDA内核函数,以及如何通过大量线程配置和协调其执行。在本章中,我们将研究如何组织和定位数据,以便通过大量线程进行高效访问。我们在第2章中讨论了数据并行计算,即数据首先从主机内存传输到设备全局内存。在第3章,可扩展并行执行中,我们确定了如何使用其块索引和线程索引来指导线程从全局内存中访问其部分数据。我们还探索了资源分配和线程调度。虽然我们涵盖的范围是一个非常好的开始,但到目前为止,我们学到的CUDA内核可能只能实现底层硬件潜在速度的一小部分。**性能不佳可归因于全局内存的长访问延迟(数百个时钟周期)和有限访问带宽,这通常使用动态随机访问存储器实现。**虽然有许多线程可用于执行,理论上可以容忍长内存访问延迟,但人们很容易遇到global内存访问路径中的流量拥塞阻止除极少数线程外的所有线程进行进度,从而使一些流式多处理器(SM)闲置。为了规避这种拥塞,CUDA提供了许多额外的资源和方法来访问内存,这些资源和方法可以删除进出全局内存的大部分流量。在本章中,您将学习使用不同的内存类型来提高CUDA内核的执行效率。
我们可以通过计算图3.8中图像模糊内核代码中执行次数最多的部分的预期性能水平来说明内存访问效率的影响。在图4.1.中复制。就执行时间而言,内核最重要的部分是嵌套的for-loop,它通过模糊补丁执行像素值累积。
在内部循环的每次迭代中,都会为一个浮点加法执行一个全局内存访问。全局内存访问获取in[]数组元素。浮点加入操作将in[]数组元素的值累积到pixVal中。因此,浮点计算与全局内存访问操作的比率为1比1或1.0。我们将把这个比率称为 compute-to-globalmemory-access,定义为为程序区域内对全局内存的每次访问执行的浮点计算数。
计算与全局内存访问比对CUDA内核的性能有重大影响。在当今的高端设备中,global内存带宽约为1000 GB/s,即1 TB/s。每个单精度浮点值有四个字节,预计每秒加载的单精度操作数不超过1000/4 = 250 giga 。计算与全局内存比为1.0时,图像模糊内核的执行将受到操作数(例如,in[] 的元素)传递到GPU的速度的限制。我们将把执行速度受内存访问吞吐量限制的程序称为memory-bound程序。在我们的示例中,内核每秒将实现不超过250千兆浮点运算(GFLOPS)。
虽然250 GFLOPS是一个可观的数字,但对于这些高端设备来说,它只是12个TFLOPS或更高的峰值单精度性能的一小部分(2%)。为了实现更高水平的内核性能,我们需要通过减少全局内存访问的数量来提高比率。要实现处理器的峰值12 TFLOPS评级,我们需要48或更高的比率。总的来说,在过去的几代设备中,所需的比率一直在增加,因为计算吞吐量比内存带宽增长得更快。本章的其余部分介绍了一种常用的减少全局内存访问数量的技术。