Skip to content

存储 ​

NVIDIA GPU 的“存储”需要从两个视角理解:物理存储层级描述数据实际经过哪些硬件,CUDA 内存空间则描述线程如何访问数据、数据对谁可见以及存活多久。两者存在映射关系,但不是一组可以逐项等同的名称。

1. 物理存储层级 ​

从靠近执行单元到设备显存,典型数据路径如下:

text
SMSP Register File
  → SM Unified L1 Data Cache / Shared Memory
  → GPU L2 Cache
  → Memory Partition / Memory Controller
  → HBM / GDDR

越靠近执行单元,容量通常越小、访问延迟越低;越靠近设备显存,容量越大,但需要更多并行请求才能隐藏延迟并利用带宽。

物理资源所在范围管理方式主要作用
Register FileSMSP / SM编译器与硬件分配保存线程操作数和执行状态
Unified L1 / Shared MemorySML1 由硬件缓存,Shared Memory 由 Kernel 显式分配保存近期数据或 Block 协作 Tile
L2 Cache整张 GPU硬件缓存,可配置部分持久化策略汇聚全部 SM 的设备内存访问
HBM / GDDRGPU Device MemoryCUDA Runtime、Allocator 和应用管理保存权重、KV Cache、Activation 与 Workspace

1.1 Register File ​

Register File 是计算单元直接使用的片上存储。CUDA C++ 中频繁访问的标量局部值通常会被编译器放入寄存器,但源码变量数量不等于最终寄存器数量;编译器会拆分、合并和复用变量。

寄存器没有缓存层级,也不需要显式 Load 到程序可见的地址空间。代价在于容量固定:每线程寄存器使用越高,一个 SM 能同时驻留的 Warp 越少。若编译器不能把值保留在寄存器中,会生成 Local Memory 访问,即 Register Spill。

1.2 Unified L1 与 Shared Memory ​

现代 NVIDIA GPU 的 L1 Data Cache 与 Shared Memory 使用同一组 Unified Data Cache 物理资源,但承担两种不同职责:

  • L1 Cache:由硬件自动缓存 Global/Local Memory 等访问,Kernel 不直接决定某个 Cache Line 的装入和淘汰。
  • Shared Memory:由 Kernel 按 Block 显式分配,程序负责装入数据、同步和复用。

部分架构允许按 Kernel 设置 L1 与 Shared Memory 的 Carveout。统一存储池容量、最大可配置 Shared Memory、单 Block 可用 Shared Memory 是不同口径,不能把产品规格中的 Unified L1/Shared 容量全部视为 Kernel 可分配空间。

Shared Memory 被划分为多个 Bank。一个 Warp 的访问若均匀落到不同 Bank,可以并行完成;若多个不同地址映射到同一 Bank,硬件会拆分请求,形成 Bank Conflict。同一地址被多个线程读取通常可以广播,不属于一般意义上的冲突。

1.3 L2 Cache ​

L2 Cache 位于 SM 与设备显存之间,由整张 GPU 的 SM 共享。Global Memory、Local Memory Spill 和部分只读数据都可能经过 L2。L2 可以降低重复访问 HBM/GDDR 的流量,但它不是某个 Block 或 Kernel 独占的空间,命中率会受并发 Kernel、数据规模和访问模式影响。

对大模型推理而言,L2 能缓存热点权重、KV Cache 元数据和中间结果,但模型权重通常远大于 L2 容量,不能假设完整模型在多次 Decode 迭代中始终保留在缓存里。

1.4 HBM 与 GDDR ​

HBM/GDDR 是离散 GPU 的主要设备显存。HBM 通过更宽的接口和多 Stack 组织提供高带宽,常见于数据中心 GPU;GDDR 使用板级显存颗粒,常见于消费级和部分工作站 GPU。

显存访问会分布到多个 Memory Partition 和 Memory Controller。控制器负责把请求映射到 Channel、Bank 和 Row,调度读写并满足 DRAM 时序。它本身不保存模型数据,而是连接 L2 与 HBM/GDDR 的访问路径。

容量和带宽解决不同问题:容量决定权重、KV Cache 和 Workspace 能否驻留在 GPU 上;带宽决定 Memory-bound Kernel 每秒能搬运多少数据。标称带宽是物理上限,实际有效带宽还取决于访问合并、缓存命中、Bank/Partition 分布和并发程度。

2. CUDA 内存空间 ​

CUDA 内存空间按访问范围、生命周期和读写语义分类。一个空间可以映射到多个物理层级,同一物理存储也可以承载不同 CUDA 空间的数据。

CUDA 空间典型物理位置可见范围生命周期主要特点
RegisterRegister File单个 ThreadThread最低访问开销,由编译器分配
Local MemoryHBM/GDDR,经 L1/L2 缓存单个 ThreadThread逻辑上私有,物理上通常不在片上
Shared MemorySM Unified Data CacheThread Block;Cluster 扩展下可跨 Block 访问Block程序显式管理、低延迟、需要同步
Global MemoryHBM/GDDR,经 L1/L2 缓存Device 上全部 Thread,Host 可通过 API 管理Allocation容量最大,是模型张量的主要存储空间
Constant MemoryDevice Memory + Constant CacheDevice 上全部 Thread,只读Allocation同一 Warp 广播相同地址时效率高
Texture MemoryDevice Memory + Texture CacheDevice 上全部 Thread,读取接口为主Allocation提供专用缓存、寻址与格式处理能力

2.1 Register 与 Local Memory ​

普通自动变量可能被编译器放入 Register,也可能因数组动态索引、变量过多或其他限制进入 Local Memory。Local 描述的是线程私有可见性,不表示数据位于线程附近的片上 SRAM。

Local Memory 访问具有设备显存级别的基础延迟,并通过 L1/L2 缓存。大型线程私有数组和 Register Spill 会增加 Local Load/Store,应结合编译器输出与 Profile 判断,而不是只根据 CUDA C++ 源码推测。

2.2 Shared Memory ​

Shared Memory 使用 __shared__ 声明,按 Block 分配:

cpp
__global__ void tiled_kernel(const float* input, float* output) {
    extern __shared__ float tile[];

    int tid = threadIdx.x;
    tile[tid] = input[blockIdx.x * blockDim.x + tid];
    __syncthreads();

    output[blockIdx.x * blockDim.x + tid] = tile[tid];
}

它适合让 Block 内线程重复使用同一 Tile、重排数据或完成归约。Shared Memory 并不会自动缓存 Global Memory;Kernel 必须显式加载数据,并在生产者与消费者之间正确同步。

2.3 Global Memory ​

Global Memory 是 CUDA Kernel 存放大规模张量的主要地址空间。对于离散 GPU,cudaMalloc 分配通常驻留在设备显存;Managed Memory、Mapped Host Memory 和多 GPU 虚拟内存会引入更复杂的数据位置,但 Kernel 仍通过统一的指针和地址空间访问。

Global Memory 的关键优化是 Coalescing。一个 Warp 中相邻线程访问相邻且对齐的数据时,硬件能用较少的内存事务服务请求;跨大步长或离散访问会搬运更多未使用字节,降低有效带宽。

2.4 Constant 与 Texture Memory ​

Constant Memory 面向只读且访问模式一致的数据。当同一 Warp 的线程读取同一地址时,Constant Cache 可以广播结果;若线程访问大量不同地址,请求可能被序列化。

Texture Memory 通过 Texture Object/Reference 提供专用缓存和寻址语义,包括特定数据格式转换、边界处理和空间局部性优化。对于普通线性张量,现代 CUDA Kernel 更常直接使用 Global Memory Load;是否使用 Texture 路径应由访问模式和 Profile 决定。

3. QA ​

几组容易混淆的关系如下:

  • Register 与 Register File:前者是编译器为线程提供的存储语义,后者是承载它的物理硬件。
  • Shared Memory 与 L1 Cache:两者共享物理池,但 Shared Memory 由程序显式管理,L1 由硬件自动管理。
  • Global Memory 与 HBM/GDDR:Global Memory 是 Device Code 看到的地址空间,HBM/GDDR 是离散 GPU 上最常见的物理承载。
  • Local Memory 与 HBM/GDDR:Local Memory 虽然线程私有,却通常落在设备显存中;“Local”描述可见范围,不描述物理距离。

4. 参考 ​

  1. NVIDIA CUDA Programming Guide:GPU Memory
  2. NVIDIA CUDA C++ Programming Guide:Memory Hierarchy
  3. NVIDIA CUDA Best Practices Guide:Device Memory Spaces
  4. NVIDIA CUDA Best Practices Guide:Coalesced Access
  5. NVIDIA CUDA Best Practices Guide:Shared Memory
  6. NVIDIA CUDA Programming Guide:Configuring L1/Shared Memory Balance
  7. NVIDIA CUDA Programming Guide:Unified and System Memory
  8. NVIDIA Hopper Architecture In-Depth