CUDA Programming Model
写在最前
本篇是简单的CUDA编程相关的入门,比较浅显易懂,如需详细了解,请转向Programming Massively Parallel Processors这本书,或者相关文章
Why is GPU 为什么选择GPU
根据计算机体系结构的差异,GPU将大量的晶体管分配制作ALU(算术逻辑单元),而CPU则大量用于控制单元.实际体现出来则是GPU对于计算更加强力.但也因此,由于缺少控制单元,GPU编程则需要将更多的精力放在逻辑控制处理上.
GPU是为了提高吞吐量而诞生的,其能够处理更多的数据量,然而代价则是其计算延迟比较大(实际上,GPU一次计算大约是4个时钟周期,而CPU仅需1个时钟周期).因此,GPU选择同时处理多个任务,通过在一个计算的延迟时间去计算其他任务来隐藏实际的延迟,在整体上体现出来GPU没有那么大的延迟.
GPU的主存通常是HBM(High Bandwidth Memory),一条HBM3E的带宽是DDR5的20倍.为了达到HBM的标称访问速度,必须做地址连续的大块访问(coalesced access),以免浪费带宽.
What is CUDA 什么是CUDA
详情请参考Programming Massively Parallel Processors,会有更详细讲解
CUDA,即Compute Unified Device Architecture,是2006年NVIDIA推出的支持GPU编程的一系列图形化API工具,使对GPU编程运算更加友好便捷.
在CUDA中,程序分配规模从小到大依次是thread,warp,block,SM,grid,其中thread是程序运行的基本单元,一个warp含有32个threads,执行相同的命令.block又叫做CTA(Cooperative Thread Array),是线程间的合作范围.SM是执行block/warp的实际硬件结构,如下图所示.
grid则是block的集合,而block的数量可以远多于SM的数量,其调度顺序是未定义的,根据实际计算情况决定.block之间都是可以独立执行的程序,没有数据依赖,是并行关系.
而对于CUDA的编译,CUDA扩展于C/C++,由nvcc进行编译.CUDA程序需要在CPU上准备数据并拷贝到GPU上,根据配置的kernal函数进行GPU上的启动计算,执行结果需要拷贝回CPU从而结束整个计算.
CUDA函数的API,详情请参考CUDA API手册
CUDA采用的是SIMT&SIMD的方式.从CC 7.0开始,每一个thread均有自己的program counter,也就是可以执行不同的指令,而SIME只有一个program counter.然而,一个warp内每次激活的线程应该具有相同的program counter.如果分支太多,将导致program counter不同的取值太多,那么SIMT也是低效的,因此最有利于SIMT发挥效率的编程方式仍然是SIMD
Memory Hierarchy 内存层次结构
Warp-Level Primitives
Warp内可以高效进行数据交换和同步,以实现reduce操作,代码如下:
1 |
|
Block内协作
Block内的协作,则采用shared memory,详情参考该文
Thread Block Cluster
从CC 9.0开始,CUDA引入了一个可选的中间层次,即Thread Block Cluster,保证Cluster中的block在同一个GPC(Graphic Processing Cluster)上调度,实现block间通信.
Hardware Implimentation of GPU GPU的硬件实现
图示为GPU的硬件结构总览
SM Structure
SM的结构则如下图(以Blackwell Ultra为例),其中,SFU(Special Function Units),实现倒数、平方、根倒数、三角函数等运算的近似计算;Tex,即Texture,用于纹理信息的处理.该SM上共有128个CUDA Core用于32位及更低进度计算
Warp调度
CUDA Core运算速度极快,但是显存的延迟是几百个时钟周期.为了防止无意义等待,Warp调度器维护着比计算单元多得多的warp,以确保一个warp因访问被阻塞的时候,调度器立刻切换到另一个可执行的warp,且上下文调度切换开销极低,这正是上文所述高吞吐掩盖延迟的具体实现操作.







