谈谈GPGPU流式多处理器架构
谈谈GPGPU流式多处理器架构
3.1 整体微架构
3.2 取指与译码
3.3 发射
3.3.1 SIMT堆栈
3.3.2 线程束调度与记分牌
3.4 操作数传送
3.4.1 线程切换导致的端口竞争
3.4.2 操作数收集器(Operand Collector)
3.5 执行与写回
3.5.1 执行单元
3.5.2 高速缓存的回写
Reference
从软件(程序员)视角阐述了线程、线程块和网格这些CUDA编程模型概念。而从硬件的角度来看,硬件将执行相同指令的线程分组到线程束(Warp)中,多个Warp构成线程块。
流式多处理器(Stream Multi-processor,SM)是构建整个 GPU的核心模块(执行整个 Kernel Grid),一个流式多处理器上一般同时运行多个线程块。每个流式多处理器可以视为具有较小结构的CPU,支持指令并行(多发射)。流式多处理器是线程块的运行载体,但一般不支持乱序执行。每个流式多处理器上的单个Warp以SIMD方式执行相同指令。
图 3‑1 流式多处理器在GPU架构中的位置(以NVIDIA Tesla架构为例,修改自NVIDIA)
3.1 整体微架构
图 3‑3是流式多处理器(SM,AMD称之为计算单元)微架构(根据公开文献和专利信息综合获得)。
流式多处理器按照流水线可以分为SIMT前端和SIMD后端。整个流水线处理划分为六个阶段,包括取指、译码、发射、操作数传送、执行与写回。
图 3‑2 GPGPU的流式多处理器结构划分
SIMD即单指令多数据,采用一个控制器来控制多组计算单元(或处理器),同时对一组数据(向量)中的每一个数据分别执行相同的操作从而实现空间并行性计算的技术。
SIMT即单指令多线程,多个线程对不同的数据集执行相同指令。SIMT的的优势在于无须把数据整理为合适的矢量长度,并且SIMT允许每个线程有不同的逻辑分支。
按照软件级别,SIMT层面,流式多处理器由线程块组成,每个线程块由多个线程束组成;SIMD层面,每个线程束内部在同一时间执行相同指令,对应不同数据,由统一的线程束调度器(Warp scheduler)调度。
一般意义上的CUDA核,对应于流处理器(SP),以计算单元和分发端口为主组成。
线程块调度程序将线程块分派给 SIMT 前端,线程在流式多处理器上以Warp为单位并行执行。
图 3‑3 GPGPU的流式多处理器微架构
流式多处理器中的主要模块包括:
- 取指单元(I-Fetch):负责将指令请求发送到指令缓存。并将程序计数器 (PC)指向下一条指令。
- 指令缓存(I-Cache):如来自取指单元的请求在指令缓存中被命中,则将指令传送给译码单元,否则把请求保存在未命中状态保持寄存器(MSHR)中。
- 译码单元(Decode):将指令解码并转发至I-Buffer。该单元还将源和目标寄存器信息转发到记分牌,并将指令类型、目标地址(用于分支)和其他控制流相关信息转发到 SIMT 堆栈。
- SIMT 堆栈(SIMT Stack):SIMT堆栈负责管理控制流相关的指令和提供下一程序计数器相关的信息。
- 记分牌(Scoreboard):用于支持指令级并行。并行执行多条独立指令时,由记分牌跟踪挂起的寄存器写入状态避免重复写入。
- 指令缓冲(I-Buffer):保存所有Warp中解码后的指令信息。 Warp 的循环调度策略决定了指令发射到执行和写回阶段的顺序。
- 后端执行单元:后端执行单元包括CUDA核心(相当于ALU)、特殊功能函数、LD/ST单元、张量核心(Tensor core)。特殊功能单元的数量通常比较少,计算相对复杂且执行速度较慢。 (例如,正弦、余弦、倒数、平方根)。
- 共享存储:除了寄存器文件,流式多处理器也有共享存储,用于保存线程块不同线程经常使用的公共数据,以减少对全局内存的访问频率。
3.2 取指与译码
图 3‑4 GPU执行流程(修改自 GPGPU-Sim)
取指-译码-执行,是处理器运行指令所遵循的一般周期性操作。
取指一般是指按照当前存储在程序计数器(Program Counter,PC)中的存储地址,取出下一条指令,并存储到指令寄存器中的过程。在取指操作结束时,PC 指向将在下一个周期读取的下一条指令。
译码一般是指将存储在指令寄存器中的指令解释为传输给执行单元的一系列控制信号。
图 3‑5 取指译码结构
在GPGPU中,译码之后要对指令进行调度,以保证后继执行单元的充分利用。这一调度通过线程束调度器(Warp Scheduler)实现。
线程束是为了提高效率打包的线程集合(NVIDIA称之为Warps,AMD称为Wavefronts)。在每一个循环中的调度单位是Warp,同一个Warp内每个线程在同一时刻执行相同命令。
取指与译码操作过程如下:
- 取指模块(I-Fetch)根据PC指向的指令,从内存中获取到相应的指令块。需要注意的是,在GPGPU中,一般没有CPU中常见的乱序执行。
图 3‑5 取指模块
- 指令缓存(I-Cache)读取固定数量的字节(对齐),并将指令位存储到寄存器中。
对I-Cache的请求会导致命中、未命中或保留失败(Reservation fail)。保留失败发生于未命中保持寄存器 (MSHR) 已满或指令缓存中没有可替换的区块。不管命中或者未命中,循环取指都会移向下一Warp。
在命中的情况下,获取的指令被发送到译码阶段。在未命中的情况下,指令缓存将生成请求。当接收到未命中响应时,新的指令块被加载到指令缓存中,然后Warp再次访问指令缓存。
- 指令缓冲(I-Buffer)用于从I-Cache中获取指令后对译码后的指令进行缓冲。最近获取的指令被译码器译码并存储在 I-Buffer 中的相应条目中,等待发射。
每个 Warp 都至少对应两个 I-Buffer。每个 I-Buffer 条目都有一个有效位(Valid)、就绪位(Ready)和一个存于此 Warp 的已解码的指令。有效位表示在 I-Buffer 中的该已解码的指令还未发射,而就绪位则表示该Warp的已解码的指令已准备好发射到执行流水线。
Cuda c编程指南
CUDA C++编程指南
CUDA 模型和接口的编程指南。
与 12.0 版相比的变化