CUDA是一个崭新的硬件和编程模型,它直接答复了这些问题并且展示了GPU 如何成 为一个真正的通用并行数据计算设备。
CUDA堆栈:
CUDA内存管理,CUDA提供DRAM内存寻址,发散/聚集内存操作,使得编程可以像CPU那样读写数据
CUDA 允许并行数据缓冲或者在On-chip 内存共享,可以进行快速的常规读写存取,在线程之间共享数据,从而减少对DRAM 内存 带宽的依赖。
编程模型
超多线程处理器
当通过CUDA 编译时,GPU 可以被视为能执行非常高数量并行线程的计算设备。它作为主CPU 的一个协处理器,一个被执行许多次不同数据的应用程序部分,可以被分离成为一个有很多不同线程在设备上 执行的函数。达到这个效果,这个函数被编译成设备的指令集(kernel 程序),机和设备使用它们自己的DRAM,主机内存和设备内存。并可以通过利用设备高性能直接内存存取(DMA) 的引擎(API)从一个DRAM 复制数据到其他DRAM。
线程批处理
线程批处理就是执行一个被组织成许多线程块的kernel;一个kernel可以合成一个线程块网格(Grid),每个线程块(Blocks)里面包含多个线程(Thread)。
当主机代码中启动一个 Kernel 时,这个 Kernel 函数将被复制到 GPU 设备上,并运行在该 Grid 中的所有线程块上。这意味着所有线程块将执行相同的 Kernel 代码,但每个线程块可能会处理不同的数据或执行稍微不同的操作,例如数据并行操作,多索引计算,参数化操作等,也可以在Kernel 中指定同步点,一个块里的线程被挂起直到它们所有都到达 同步点。
动态并行度调整
UDA允许在编写 Kernel 时不指定特定的硬件配置或线程块大小。相反,您可以使用内置的线程块和线程索引变量,使 Kernel 能够适应运行它的设备的配置。这样,相同的 Kernel 可以在不同设备上运行,并根据每个设备的能力进行自动调整。这个模式允许kernel 用不同的并行能力有 效地运行在各种设备上而不用再编译。
内存模型
读写每条线程的寄存器;读写每条线程的本地内存;读写每个块的共享内存;读写每个栅格的全局内存;只读每个栅格的常量内存, 只读每个栅格的纹理内存。
GPU全局内存、常量内存、纹理内存的区别:
- • 全局内存:大容量内存,用于存储数据,包括模型参数,输入参数、输出数据等。访问全局内存需要较长的访问延迟;全局内存通常用于多个线程块之间的共享数据
- • 常量内存:常量内存是一种只读内存,用于存储常来给你数据。通常用于不会被修改的数据,例如预计算常量访问常量内存会比全局内存快
- • 纹理内存:纹理内存专门用于纹理映射和采样的内存,通常用于图像学处理任务纹理内存提供高速的采样和过滤功能,以支持图像处理任务
硬件实现
带有on-chip的SIMD多处理器
每个多处理器使用单一指令,多数据架构(SIMD) :在任 何给定的时钟周期内,多处理器的每个处理器执行同一指令,但操作不同的数据。如下图模型
执行模式
一个Grid是通过多处理器规划执行的。每个多处理器可以批处理多个块。一个块只被一个 多处理器处理,因此可以对驻留在on-chip 共享内存中的共享内存空间形成非常快速的访问。
一个多处理器(SM)可以处理多少个块,取决于每个线程中分配了多少个分配了多少个寄存器,以及每个时钟片需要多少内存。如果两者其中一个不足,也会导致该线程块无法启动。下图为SM分配blcok:
线程块在一个批处理中被一个多处理器执行,被称作active。每个active 块被划分成为SIMD 线程组,每个线程组称为warp,在NVIDIA GPU中,一个warp 通常由32个线程组成。
线程调度程序周期性地从一条warp 切换到另一条warp,以达到多处理器计算资源使用的最大化。
一个多处理器可以处理并发地几个块,通过划分在它们之中的寄存器和共享内存。更准确地说,每条线程可使用的寄存器数量,等于每个多处理器寄存器总数除以并发的线程数量,并发线程的数量等于并发块的数量乘以每块线程的数量。
在一个块内的warp 次序是未定义的,但通过协调全局或者共享内存的存取,它们可以同步 的执行。如果一个通过warp 线程执行的指令写入全局或共享内存的同一位置,写的次序是 未定义的。
在一个线程块栅格内的块次序是未定义的,并且在块之间不存在同步机制,因此来自同一个栅格的二个不 同块的线程不能通过全局内存彼此安全地通讯