CUDA程序优化
CUDA程序优化应该考虑的点:
精度:只在关键步骤使用双精度,其他部分仍然使用单精度浮点以获得指令吞吐量和精度的平衡;
延迟:需要首先缓冲一部分数据,缓冲的大小应该可以保证每个内核程序处理的一批数据能够让GPU慢负荷工作;
计算量:计算量太小的程序使用CUDA很不合算;当需要计算的问题的计算密集度很低的时候,执行计算的时间远远比IO花费的时间短,整个程序的瓶颈出现在PCI-E带宽上。
优秀的CUDA程序特征:
在给定的数据规模下,选用算法的计算复杂度不明显高于最优的算法;
Active warp的数量能够让SM满载,并且active block的数量大于2,能够有效地隐藏访存延迟;
当瓶颈出现在运算指令时,指令流的效率已经过了充分优化;
当瓶颈出现在访问IO时,程序已经选用了恰当的存储器来储存数据,并使用了适当的存储器访问方式,以获得最大带宽;
CUDA的编写与优化需要解决的问题:
确定任务中的串行和并行的部分,选择合适的算法;
按照算法确定数据和任务的划分方式,将每个需要实现的步骤映射为一个满足CUDA两层并行模型的内核函数,让每个SM上至少有6个活动warp和至少2个活动block;
在精度不组或者发生一处时必须使用双精度浮点或者更长的整数类型;
优化显存访问:合并采用相同block和grid的kernel;尽力避免将线程私有变量分配到local memory;
优化指令流:在误差可接受的情况下,使用CUDA算术指令集中的快速指令;避免多余的同步;在只需要少量线程进行操作的情况下,使用类似“if threaded<N”的方式,避免多个线程同时运行占用更长时间或者产生错误结果;
资源均衡:调整每个线程处理的数据量,shared memory和register和使用量;通过调整block大小,修改算法和指令以及动态分配shared memory,都可以提高shred的使用效率;register的多少是由内核程序中使用寄存器最多的时刻的用量决定的,因此减小register的使用相对困难;
节约register方法:使用shared memory存储变量;使用括号明确地表示每个变量的生存周期;使用占用寄存器较小的等效指令代替原有指令;
与主机通信优化:尽量减少CPU与GPU间的传输:使用cudaMallocHost分配主机端存储器,可以获得更大带宽;一次缓存较多的数据后再一次传输,可以获得较高的贷款;需要将结果显示到屏幕的时候,直接使用与图形学API互操作的功能;使用流和异步处理隐藏与主机的通信时间;使用zero-memory技术和Write-Combined memory提高可用带宽;
测量程序运行时间:
CUDA内核程序的运行时间:可以在设备端测量,也可以在主机端测量;
CUDA API的运行时间:只能在主机端测量;使用CUDA runtime API时,会在第一次调用runtime API函数时启动CUDA环境,计时的时候应该避免将这一部分计入,因此在正式测试之前应当首先及你选哪个一侧包含数据输入输出地就爱上你,使得GPU从平时的节能模式进入工作状态,使得测试结果更加可靠;
设备端测量时间: