title: CUDA程序调优指南(二):性能调优 - 知乎.md
toc: true
date: 2021-12-22 09:25:00
https://zhuanlan.zhihu.com/p/84510732?from_voters_page=true
本系列文章是我阅读CUDA官方文档以及实践经验所总结而出,如有错误和不足,还请多多指出
目录:
对于一个CUDA kernel function而言,其通常由如下几个部分组成:
__syncthreads__
call<<<BlocksNum, ThreadsNumPerBlock>>>
我们分别考虑如何对这些部分进行优化。
__global__
function的参数是存在constant mem里的,并且其大小被限制为4KB。
通常来说,我们传入的参数都比较少,因此这些参数大部分是直接缓存在SM上的register上的,因此读取起来最快。但如果register不够用,那么就会被放到constant mem/cache上,速度就会变慢。
对于单独的local var,其会被放在register里面,因此读写极快。
对于数组类型的local var,根据访问pattern的不同,速度也不同(见per-thread array的访问)
总结而言是这样的:
__syncthreads__
callshared memory就如同2.3节所述,我们需要尽量避免bank confilicts,这样读取最快,一个cycle clock就可以读取128byte。
除此之外,因为shared memory由同一block内的thread共享,所以在初始化shared mem之后,需要调用__syncthreads__
来对同一block内的所有threads进行同步。
编译器会自行决定device function是否会被inlined(多数情况下会inline)。通常来说,inline会更好,因为少了函数调用的开销。
我们可以用**forceinline**来给编译器提示
在书写kernel function时,应尽量避免loop/if,因为这两个在代码里引入了分支结构。
如果代码有分支,那么
因此在可能的情况下,我们应该尽量使用模板参数来替换掉loop/if:
if
所判断的条件在kernel launch时就能确定(即作为kernel的一个parameter传入的),那就可以用模板参数来代替该parameter,这样编译器会自动优化掉另一分支。针对一些边界是常量(如0->5)的循环,在循环体足够简单的情况下,可以使用#pragma unroll
来告诉编译器展开该循环。
朱泙漫学屠龙于支离益,单千金之家。三年技成,而无所用其巧 —《庄子·列御寇》
BlocksNum
和ThreadsNumPerBlock
是执行kernel function时配置的值。这两个值通常都是经验求解,很难找到最优值。
简单来说,
ThreadsNumPerBlock
受限于device property的MaxThreadsPerBlock
,经验取值为512/1024。BlocksNum
最大无限制,常见求解公式为 。更详细的见《CUDA程序调优指南(三):BlockNum和ThreadNumPerBlock》