本节书摘来自华章计算机《CUDA C编程权威指南》一书中的第2章,第2.2节,作者 [美] 马克斯·格罗斯曼(Max Grossman),译 颜成钢 殷建 李亮,更多章节内容可以访问云栖社区“华章计算机”公众号查看。
2.2 给核函数计时
在内核的性能转换过程中,了解核函数的执行需要多长时间是很有帮助并且十分关键的。衡量核函数性能的方法有很多。最简单的方法是在主机端使用一个CPU或GPU计时器来计算内核的执行时间。在本节,你需要设置一个CPU计时器,并学习使用NVIDIA分析工具来计算执行时间。第6章将教你如何使用CUDA特定的计时程序。
2.2.1 用CPU计时器计时
可以使用gettimeofday系统调用来创建一个CPU计时器,以获取系统的时钟时间,它将返回自1970年1月1日零点以来,到现在的秒数。程序中需要添加sys/time.h头文件,如代码清单2-5所示。
你可以用cpuSecond函数来测试你的核函数:
由于核函数调用与主机端程序是异步的,你需要用cudaDeviceSynchronize函数来等待所有的GPU线程运行结束。变量iElaps表示程序运行的时间,就像你用手表记录的核函数的执行时间(用秒计算)。
现在,通过设置数据集大小来对一个有16M个元素的大向量进行测试:
由于GPU的可扩展性,你需要借助块和线程的索引来计算一个按行优先的数组索引i,并对核函数进行修改,添加限定条件(i<N)来检验索引值是否越界,如下所示:
有了这些更改,可以使用不同的执行配置来衡量核函数。为了解决创建的线程总数大于向量元素总数的情况,你需要限制内核不能非法访问全局内存,如图2-7所示。
代码清单2-5展示了如何在主函数中用CPU计时器测试向量加法的核函数。
默认的执行配置被设置为一个包含16 384个块的一维网格,每个块包含1 024个线程。用以下命令编译并运行程序:
在基于英特尔Sandy Bridge架构的系统上进行测试,从代码清单2-5的示例中可以看出,在GPU上进行的向量加法的运算速度是在CPU上运行向量加法的3.86倍。
把块的维度减少到512可以创建32 768个块。在这个新的配置下,内核的性能提升了1.19倍。
如果进一步将块的维度降低到256,系统将提示以下错误信息,信息表示块的总数超过了一维网格的限制。
2.2.2 用nvprof工具计时
自CUDA 5.0以来,NVIDIA提供了一个名为nvprof的命令行分析工具,可以帮助从应用程序的CPU和GPU活动情况中获取时间线信息,其包括内核执行、内存传输以及CUDA API的调用。其用法如下。
可以使用以下命令获取更多关于nvprof的帮助信息:
你可以用如下命令去测试内核:
nvprof的输出结果会因你使用的GPU类型不同而有所差异。以下结果是从Tesla GPU中得到的:
以上结果的前半部分来自于程序的输出,后半部分来自于nvprof的输出。可以注意到,CPU计时器显示消耗的内核时间为3.26ms,而nvprof显示消耗的内核时间为2.90ms。在这个例子中,nvprof的结果更为精确,因为CPU计时器测量的时间中包含了来自nvprof附加的时间。
nvprof是一个能帮助你理解在执行应用程序时所花费的时间主要用在何处的强大工具。可以注意到,在这个例子中,主机和设备之间的数据传输需要的时间比内核执行的时间要多。图2-8所描绘的时间线(未按比例绘制),显示了在CPU上消耗的时间、数据传输所用的时间以及在GPU上计算所用的时间。
对于HPC工作负载,理解程序中通信比的计算是非常重要的。如果你的应用程序用于计算的时间大于数据传输所用的时间,那么或许可以压缩这些操作,并完全隐藏与传输数据有关的延迟。如果你的应用程序用于计算的时间少于数据传输所用的时间,那么需要尽量减少主机和设备之间的传输。在第6章中,你将会学习如何使用CUDA流和事件来压缩计算量和通信量。