内存模型

0. L1/L2

每个 SM 有自己独有的一片存储空间,这个空间被 Shared memory 和 L1 缓存公用。而 L2 缓存 被所有 SM 公用, L2 中缓存来自 Global Memory 中的数据。

SP 访存时,访问数据时的确遵循一个层次结构。 首先尝试从寄存器 (Registers) 中获取数据,若没有找到则从 L1 中找数据,若没有则从 L2 中找,若仍没有,从 Global Memory 中找。

1. 寄存器 (Registers):

寄存器内存 Bank 冲突: CUDA 的多处理器 (SM) 中的寄存器被组织成多个 Bank。如果多个线程同时访问同一个 Bank 中的寄存器,就会发生 Bank 冲突。这会导致访问被串行化,而不是并行化,从而降低性能。 这与共享内存的 Bank 冲突类似,但发生在寄存器级别。应用程序无法直接控制 Bank 冲突。编译器和硬件会尝试优化以减少冲突,但无法完全避免。

特性: 每个线程拥有私有的寄存器,速度最快,但数量有限。 最佳实践: 用于存储线程频繁访问的局部变量。如果寄存器数量不足(超过 SM 可用的寄存器数量时),编译器会将变量溢出到局部内存。

寄存器是每个线程专用的高速片上存储。一个寄存器的标准大小是32位。由于 float 数据类型通常也是 32 位,因此一个寄存器可以存储 一个 float 值。 大小不一定,不同的架构的寄存器的位数不一样。

2. 局部内存 (Local Memory):

特性:

  • 每个线程拥有私有的局部内存空间,速度比全局内存快,但比寄存器慢,且数量有限。
  • 存在于 SM or Global(?),生命周期是thread。
  • 存储内容:它存储编译器无法放入寄存器的那些局部变量。这包括但不限于大型结构体或数组。关键在于寄存器空间的限制。如果一个变量(无论是否是自动变量)太大或线程使用的寄存器过多导致溢出(register spilling),编译器就会将该变量放入局部内存。***
  • 所以编译器自动管理: 开发者通常无需直接操作局部内存。

最佳实践: 当寄存器不足时,编译器会自动将变量溢出到局部内存。通过使用更小的变量、优化算法以减少局部变量的需求来减少局部变量的占用的空间。***

技巧:有个技巧,如果一个 kernel 函数中需要使用数组,而且数组的长度是固定的,为了避免使用 Local memory ,将这个数组拆成单个的变量,这些变量会被存储到 Registers 中(当然是当 Registers 的个数足够时)

3. 共享内存 (Shared Memory):

特性: 每个线程块拥有共享的共享内存空间,速度比全局内存快,但比寄存器和局部内存慢。 共享内存可以用于线程间通信和数据共享。 最佳实践: 用于在同一线程块内的线程之间共享数据,以减少对全局内存的访问,提高性能。 有效利用共享内存需要仔细规划内存访问模式,以避免冲突和等待。

4. 常量内存 (Constant Memory):

大小: CUDA 设备上的常量内存总大小通常为 64KB。 只读: 常量内存是只读的。 Warp 级别访问: 当一个 warp 中的所有线程访问常量内存的同一个地址时,访问速度非常快,接近寄存器访问速度。这是因为常量内存被缓存,并且硬件可以广播单个内存读取到 warp 中的每个线程。换句话说:常量内存最适合于广播模式,即所有线程访问相同的数据。

它适用于在内核执行期间保持不变的数据。这些数据可以是全局的,也可以是局部于内核的,只要在内核执行期间不发生改变即可。 最佳实践: 用于存储在内核执行期间不会改变的常量数据。 常量内存具有缓存机制,如果所有线程访问相同的常量内存地址,则访问速度非常快。

5. 全局内存 (Global Memory):

特性: 所有线程都可以访问的全局内存空间,速度最慢,但容量最大。 最佳实践: 用于存储大型数据集,以及需要在不同线程块之间共享的数据。 为了提高性能,应尽量减少对全局内存的访问,并使用 coalesced memory access (合并内存访问) 技术。

6. 纹理内存 (Texture Memory):

特性: 只读内存,具有特殊的缓存机制,适合于访问具有空间局部性的数据。 最佳实践: 用于存储图像数据或其他具有空间局部性的数据。 纹理内存的缓存机制可以提高访问速度。

7. 主机内存 (Host Memory):

特性: CPU 可访问的内存。 最佳实践: 用于存储输入和输出数据。 为了提高性能,应使用 pinned memory (固定内存) 或 page-locked memory (页锁定内存) 来减少数据传输的开销。 使用 cudaMallocHost 分配 pinned memory,使用 cudaHostAlloc 分配 page-locked memory。page-locked memory 是 pinned memory 的一种更严格的子集