NV GPU硬件细节
主要硬件组成
| 模块 | 功能 |
| SM(Streaming Multiprocessors) | 执行核函数(Kernel)计算任务(即 compute) |
| Copy Engines(DMA Engines) | 执行内存拷贝任务,如 Host ↔ Device,Device ↔ Device |
| Memory Controller | 管理对 global memory(DRAM)的访问请求 |
| PCIe/NVLink 接口 | 执行跨设备或主机拷贝 |
Streaming Multiprocessor(SM,流式多处理器)
每个 Streaming Multiprocessor(SM,流式多处理器) 上有多个 CUDA 核心。比如一个 SM 可能有 64、128 或更多 CUDA 核心(具体取决于架构)
block(的各个线程)不是直接“映射”到核心上
当你创建一个 block(比如 1024 个线程),这些线程被分为若干个 warp(32 个线程一组)
每次只执行 一个或几个 warp,其余的等待调度
SM 使用内部的 warp scheduler 按顺序调度这些 warp 到 CUDA 核心执行
总结:一个 block 最多可以有 1024 个“线程”,但这些线程是被调度到“共享的 CUDA 核心”上轮流执行的,而不是每个线程配一个核心
常见问题:所以threads数量和CUDA Core数量一致就能保证最大可能地利用硬件吗?不一定,threads过多过少都可能影响效率,好像没有一个一般性的选择标准?当然和warp数量(32)对齐是一定要保证的。
线程数越多(在1024的block上限内、且满足资源限制的前提下)通常越能让SM的计算核心得到更充分利用
一个block的线程越多:可以形成越多的warp(每32个线程一个warp),形成越多的warp可以提升指令发射覆盖延迟能力
但是线程调度也会带来性能消耗?每个线程都有寄存器分配、调度、上下文管理成本
让每个thread的计算“粒度”适中,即:不要太轻,不然你在浪费线程管理的成本;也不要太重,不然并行度不足
SM内部是通过 warp-level 调度的:SM每个cycle可以调度多个warp执行指令
不同显卡(不同架构)的 SM 每个时钟周期可以调度的 warp 数量是不一样的
现代GPU(如Volta/Ampere)每个SM每周期能调度多个warp,而且调度器(warp scheduler)之间是并行工作
每 32 个线程组成一个 warp,warp scheduler 把 warp 分配给 CUDA Core 来执行
绝大多数现代NVIDIA架构,SM的CUDA Core数量和warp调度器数量是配套增长的。
一个warp调度器,通常负责一组32个CUDA Core。
比如,Volta/Ampere架构每个SM有64~128个CUDA Core,4个warp调度器。
4个调度器 × 32 Core = 128个Core,这样每个调度器都能每周期各自发射一个warp,所有Core都能被充分利用。
SM里有多少Core,每个周期最多能干多少活,线程多了就是排队等着。
这就是为什么线程太多不会提升单个Block的性能,只会让排队时间变长。
但是合适增加warp数量可以更好地隐藏访存延迟,这是为什么推荐用1024线程/Block来提高SM利用率。
SM(Streaming Multiprocessor)
├── Warp Scheduler × N
├── CUDA Core × 64/128(FP32 ALU)
├── Special Function Units(SFU)
├── Load/Store Units(LD/ST)
├── Tensor Cores(如果有)
├── Shared Memory / L1 Cache
如何理解Streaming?「流」指的是什么?
首先,每个CUDA Core都是一样的,没有区别,不是CPU那样的流水线,有的取指令、有的计算之类的
在一个 SM 内的所有 CUDA Core,本质上是一组功能完全一样的 ALU(算术逻辑单元)。它们没有彼此之间的前后依赖关系,不存在“你处理前半,我处理后半”的流水线那种分工
CUDA Core 之间是“平行关系”,而不是“前后关系”
“Streaming”是逻辑概念,不是流水线关系
数据(线程)一批批地流入 CUDA Cores,按统一指令执行,然后流出,下一批继续
并不是流水线的流动,而是整体调度连续不断
CUDA核心
CUDA 核心是 GPU 上执行浮点/整数运算的最小硬件单元(FP32 ALUs)
一个thread在一个CUDA Core上执行,一个warp = 32 threads
Copy Engines
Kernel 计算 是由 SMs 执行的
数据 Copy(特别是异步的 cudaMemcpyAsync)则是由 DMA copy engine(也叫 Copy Engine) 完成的,不占用 SMs
二者可以并发执行,前提是你的 GPU 支持 deviceOverlap(使用cudaGetDeviceProperties查询)
虽然技术上 copy 和 kernel 可以并发,但也可能因为 共享资源(如 memory bandwidth) 导致性能下降:
同时进行大规模 copy 和计算时,可能发生 带宽争用
比如 copy 过程中 saturate 了 DRAM 带宽,kernel 访问 global memory 会变慢
Copy Engine最多3个
Host-to-Device、Device-to-Host、Peer-to-Peer 之间可以并发
但是某一个特定方向(比如Host-to-Device)内部不可并发(应该)