跟着实验室打酱油,涉及到了CUDA编程,这里记录一些零碎的经验和资料。作为新手踩了不少奇奇怪怪的坑。
我们做的事情有点奇怪,本来是在CPU上做图的DFS,现在想移植到CUDA上。GPU其实不太适合用来做这个事情,但是图很大,而且涉及到很多集合求交和求差集等操作,还是有点希望的。
递归
首先是CUDA上线程的栈空间不算大。随手试了一下,记得是递归40多层就炸了;不过我们的递归深度层数不多,就硬上了递归。直接函数递归会使得编译器无法得知函数使用的寄存器数量,所以局部变量就spill到了全局内存里,会对性能有较大的影响。目前的解决方案是手动指定递归的最大深度,然后把深度作为函数模板的模板参数,让编译器在编译期进行递归展开,这样就能知道寄存器的使用量。
寄存器用量可以通过编译选项得到。给nvcc添加
–ptxas-options=-v
即可。
Occupancy
函数的寄存器、共享内存用量会影响到占用率。有一段时间程序的occupancy受到了寄存器数量的约束,有的时候多写了些实际并没有执行的代码,但由于寄存器用量增加,会导致程序性能下降。遇到这种情况可以用CUDA提供的occupancy计算表格得到一些理论结果,若发现某些参数存在较为明显的边际效应,可以考虑对资源用量进行限制。nvcc的-maxrregcount=N
选项可以限制最大寄存器数量。
如有需要可以在程序中计算出最大可执行的block/warp数量。cudaDeviceGetAttribute(&number_of_sms, cudaDevAttrMultiProcessorCount, device)
得到GPU的SM数量,然后cudaOccupancyMaxActiveBlocksPerMultiprocessor(&max_active_blocks_per_sm, kernel, number_of_threads_per_block, block_shared_mem_size)
得到对给定核函数每个SM上最大活跃block数量。
剩下的有心情再写。