第 4 章:GPU 内存系统设计
图形处理单元 (GPU) 已经发展成为高度并行、可编程的加速器,能够在广泛的应用程序上实现高性能和高能效。内存系统是现代 GPU 体系结构的关键组件,因为它必须为大量并发线程提供对数据的快速访问。在本章中,我们将探讨 GPU 内存系统设计的关键元素,包括一级内存结构、片上互连网络、内存分区单元以及未来 GPU 内存系统的研究方向。
一级内存结构
GPU 中的一级内存结构负责提供对频繁使用的数据的快速访问,并减少对内存层次结构较低层次的访问。这些结构通常包括刮擦板内存、L1 数据缓存和 L1 纹理缓存。
刮擦板内存和 L1 数据缓存
刮擦板内存,也称为 NVIDIA 的 CUDA 编程模型中的共享内存或 OpenCL 中的本地内存,是一种低延迟、软件管理的内存空间,由合作线程阵列 (CTA) 或工作组中的所有线程共享。刮擦板内存通常使用分行 SRAM 结构实现,以实现多个线程的并行访问。
图 4.1 说明了一个统一的 L1 数据缓存和刮擦板内存组织,类似于 NVIDIA 的 Fermi 和 Kepler 架构中使用的设计 [Minkin et al., 2012]。
地址交叉开关
|
v
数据阵列 (可配置为刮擦板或缓存)
|
v
数据交叉开关
|
v
加载/存储单元
```图 4.1: 统一的 L1 数据缓存和刮擦板内存组织。
这种设计的关键组件包括:
1. **数据阵列**: 一个高度分行的 SRAM 结构,可以配置为刮擦板内存或 L1 数据缓存。每个银行都是 32 位宽,并有自己的解码器,可以独立访问。
2. **地址交叉开关**: 将来自加载/存储单元的内存地址分配到数据阵列中的适当银行。
3. **数据交叉开关**: 将数据从银行路由到加载/存储单元,后者将数据写入寄存器文件。
4. **加载/存储单元**: 计算内存地址,应用合并规则,并将内存访问分解为单个合并访问。
刮擦板内存访问绕过标签查找阶段,因为内存是直接映射的。通过将冲突的访问分成多个周期来处理银行冲突,在后续周期中重放冲突的部分。
L1 数据缓存用于存储全局内存地址空间的子集。对 L1 数据缓存的访问涉及标签查找,以确定请求的数据是否存在。缓存块大小通常为 128 字节,可以进一步划分为 32 字节的扇区,以匹配可以从图形 DRAM (例如 GDDR5) 单次访问的最小数据大小。
### L1 纹理缓存
纹理内存是一种针对空间局部性进行优化的只读内存空间,通常用于图形工作负载。L1 纹理缓存旨在利用纹理访问中存在的 2D 空间局部性。
图 4.2 显示了一个典型的 L1 纹理缓存组织。纹理过滤
|
v
过滤后的纹理像素
图4.2: L1纹理缓存组织。
L1纹理缓存的主要组件包括:
-
地址映射: 将纹理坐标转换为缓存地址。
-
标签数组: 存储每个缓存行的标签,以确定请求的数据是否存在。
-
数据数组: 存储实际的纹理数据。
-
纹理过滤: 对获取的纹理数据执行插值和过滤操作,生成最终的过滤后的纹理像素。
L1纹理缓存通常采用基于瓦片的组织方式,利用空间局部性。缓存被划分为较小的瓦片(例如4x4或8x8个纹理像素),每个瓦片以连续的方式存储,以最小化给定纹理获取所访问的缓存行数。
统一的纹理和数据缓存
最近的GPU架构,如NVIDIA的Maxwell和Pascal,引入了统一的纹理和数据缓存,以提高缓存利用率并减少总体缓存占用[Heinrich et al., 2017]。在这种设计中,L1数据缓存和L1纹理缓存被合并为单个物理缓存,能够根据工作负载的需求动态分配容量。
图4.3说明了统一的纹理和数据缓存组织。
内存请求
|
v
缓存控制器
/ \
/ \
/ \
v v
数据缓存分区 纹理缓存分区
|这是一个统一的纹理和数据缓存组织的示意图。主要组件包括:
1. **缓存控制器**:接收内存请求,并确定是由数据缓存分区还是纹理缓存分区来处理。
2. **数据缓存分区**:处理对全局内存空间的访问,类似于独立的L1数据缓存。
3. **纹理缓存分区**:处理纹理内存访问,类似于独立的L1纹理缓存。
4. **数据阵列**:一个共享的数据阵列,存储全局内存数据和纹理数据。
统一缓存设计允许更好地利用可用的缓存容量,因为分区大小可以根据工作负载的访问模式进行调整。这种灵活性可以带来更好的性能和能源效率,相比于固定大小的独立L1缓存。
## 片上互连网络
片上互连网络负责将GPU核心(也称为流式多处理器或计算单元)连接到内存分区单元。互连网络必须提供高带宽和低延迟,以支持GPU工作负载中的大规模并行性。
现代GPU通常采用交叉开关或网格拓扑用于片上互连。交叉开关提供了所有核心和内存分区之间的完全连通性,实现了高带宽通信,但代价是增加了面积和功耗。网格拓扑则通过将每个核心连接到其相邻的核心和内存分区,形成网格状结构,提供了更可扩展的解决方案。
图4.4展示了GPU中网格互连的一个示例。| |
核心 核心 核心 核心
| | | |
—— —— —— ——
| | | |
核心 核心 核心 核心
| | | |
—— —— —— ——
| | | |
内存 内存 内存 内存
分区 分区 分区 分区
图4.4: GPU中的网格互连。
网格互连允许在核心和内存分区之间进行高效的数据传输,同时最小化面积和功耗开销。采用先进的路由算法和流量控制机制,以确保高性能并避免拥塞。
内存分区单元
内存分区单元负责处理来自GPU核心的内存请求,并管理离线DRAM。每个内存分区通常包括L2缓存、原子操作支持和内存访问调度器。
L2缓存
L2缓存是一个共享缓存,位于GPU核心和离线DRAM之间。其主要目的是通过缓存频繁访问的数据,减少对高延迟、高能耗DRAM的访问。
GPU L2缓存通常被设计为一个集联、写回的缓存,具有大容量(例如2-4MB)和高带宽。L2缓存被分区到多个内存分区中,以实现并行访问和提高吞吐量。
图4.5说明了GPU内存分区中L2缓存的组织结构。
内存请求
|
v
L2缓存控制器
|
v
标记数组
|
v
数据数组
|
v
内存调度器
```这是一个关于 DRAM 和 GPU 内存分区的 Markdown 文件。以下是中文翻译:
图 4.5: GPU 内存分区中的 L2 缓存组织。
L2 缓存控制器接收来自 GPU 核心的内存请求,并检查标记数组以确定请求的数据是否存在于缓存中。在缓存命中的情况下,数据从数据数组中检索并发送回请求的核心。在缓存未命中的情况下,请求被转发到内存调度器,然后从 DRAM 获取数据。
原子操作
原子操作对于并行工作负载中线程之间的同步和通信至关重要。GPU 支持各种原子操作,如原子加法、最小值、最大值和比较并交换,这些操作可以保证多个线程同时访问同一内存位置时的原子性。
原子操作通常在内存分区单元中实现,以确保低延迟和高吞吐量的执行。使用专用硬件单元,如原子操作单元 (AOU),来高效地处理原子请求。
图 4.6 展示了 GPU 内存分区中的原子操作单元示例。
原子请求
|
v
原子操作单元
|
v
L2 缓存/DRAM
图 4.6: GPU 内存分区中的原子操作单元。
AOU 接收来自 GPU 核心的原子请求,并对目标内存位置执行请求的操作。如果内存位置存在于 L2 缓存中,AOU 直接更新缓存数据。如果内存位置未缓存,AOU 从 DRAM 获取数据,执行原子操作,然后将结果写回 DRAM。
内存访问调度器
内存访问调度器负责管理以下是该 Markdown 文件的中文翻译版本。对于代码部分,我只翻译了注释,代码本身没有翻译。
将内存请求发送到片外 DRAM。它的主要目标是最大化 DRAM 带宽利用率,同时最小化内存访问的延迟。
GPU 内存调度器采用各种调度算法和优化技术来实现高性能。一些常见的技术包括:
-
乱序调度:重新排序内存请求,以最大化行缓冲区命中率,并最小化 DRAM 预充电和激活开销。
-
银行级并行:利用多个 DRAM 银行之间的并行性,实现对不同内存区域的并发访问。
-
写入到读取转换优化:最小化在 DRAM 中切换写入和读取操作时产生的延迟惩罚。
-
地址交织:将内存访问分散到不同的通道、等级和银行,以最大化并行性并避免争用。
图 4.7 展示了 GPU 内存分区中内存访问调度器的高级视图。
内存请求
|
v
内存调度器
|
v
通道 通道 通道 通道
| | | |
v v v v
等级 等级 等级 等级
| | | |
v v v v
银行 银行 银行 银行
图 4.7: GPU 内存分区中的内存访问调度器。
内存调度器从 L2 缓存和原子操作单元接收内存请求,并决定何时以及以何种顺序将这些请求发送到 DRAM。通过仔细调度内存访问,调度器可以显著提高 DRAM 带宽利用率,并减少平均内存访问延迟。
研究作为 GPU 架构不断发展,并且并行工作负载的需求不断增加,有几个研究方向旨在提高 GPU 内存系统的性能和效率。一些主要的研究领域包括:
内存访问调度和互连网络设计
随着 GPU 中核心数量和内存分区的不断增加,内存访问调度器和互连网络的设计对于实现高性能至关重要。这个领域的研究集中在开发新颖的调度算法和互连拓扑,以有效处理 GPU 工作负载的大规模并行性和复杂的内存访问模式。
例如,Jia 等人 [2012] 提出了一种称为"分阶段内存调度"(SMS)的内存调度算法,旨在提高 DRAM 银行级并行性并减少内存访问延迟。SMS 将内存请求队列分为两个阶段:批处理形成和批处理调度。在批处理形成阶段,请求根据它们的银行和行地址分组,以利用行局部性。在批处理调度阶段,根据批次的年龄和关键性对批次进行优先级排序,以确保公平性并减少停顿。
另一个例子是 Kim 等人 [2012] 提出的用于 GPU 的高带宽内存(HBM)架构。HBM 将多个 DRAM 芯片堆叠在彼此之上,并通过硅通孔(TSV)进行连接,从而实现了比传统 GDDR 内存高得多的带宽和更低的延迟。作者还提出了一种新颖的内存控制器设计,可以有效管理 HBM 的增加的并行性和复杂性。
缓存效率
GPU 采用各种缓存机制来减少离线内存访问次数,提高性能。但是,这些缓存的效率可能会因工作负载的特性和缓存设计而显著不同。这个领域的研究旨在通过缓存旁路、缓存压缩和自适应缓存管理等技术来提高GPU缓存的效率。
例如,Huangfu和Xie[2016]提出了一种针对GPU的动态缓存旁路方案,使用一种简单而有效的启发式方法来决定是否将内存请求缓存或旁路,这种方案根据内存请求的重用距离进行自适应,可以显著减少缓存污染并提高性能。
另一个例子是Vijaykumar等人[2015]提出的GPU压缩缓存架构。作者观察到许多GPU应用程序表现出显著的数据冗余,这可以被利用来增加缓存的有效容量。他们提出了一种新颖的压缩方案,可以实现高压缩率,同时只有很小的延迟开销。
内存请求优先级和缓存旁路
在GPU中,来自不同线程块和线程的内存请求可能具有不同的重要性和对整体性能的影响。优先处理关键请求并旁路非关键请求可以帮助减少内存延迟,提高资源利用率。
这个领域的研究探索了识别和优先处理关键内存请求的技术,以及选择性地旁路缓存的机制。
例如,Jog等人[2013]提出了一种称为"关键感知线程块加速"(CAWA)的内存请求优先级方案。CAWA识别可能会导致管线停滞的关键线程块,并优先处理它们的内存请求。该方案结合使用静态和动态信息,如依赖指令数量和线程块的年龄,来确定重要性。
Lee等人[2015]提出了一种针对GPU的缓存旁路方案,旨在减少缓存污染并提高内存访问的及时性。该方案使用基于程序计数器的预测模型来决定是否旁路缓存。这个 Markdown 文件的中文翻译如下:
预测机制来识别不太可能从缓存中获益的内存请求,并将它们直接绕过到较低级别的内存层次。作者表明,与没有绕过的基线 GPU 相比,他们的方案可以显著提高性能和能源效率。
利用线程组间异质性
GPU 同时执行大量的线程组,以隐藏内存延迟并实现高吞吐量。然而,不同的线程组在资源需求、内存访问模式和性能特征方面可能存在显著的异质性。
这个领域的研究旨在利用这种线程组间异质性来改善 GPU 中的资源分配、调度和内存管理。
例如,Kayıran 等人[2014]提出了一种线程组级别的分歧感知缓存管理方案,它根据每个线程组的分歧特性动态调整缓存分配和替换策略。高分歧的线程组被分配更多的缓存资源以减少内存分歧,而低分歧的线程组被分配较少的资源以提高缓存利用率。
另一个例子是 Sethia 等人[2015]的工作,提出了一种内存控制器设计,利用线程组间异质性来提高 DRAM 银行级并行性。作者观察到不同的线程组可能具有不同程度的银行级并行性,并提出了一种面向线程组的内存调度算法,优先调度具有高银行级并行性的线程组,以减少内存争用并提高系统吞吐量。
协调的缓存绕过
缓存绕过是一种允许内存请求跳过缓存并直接访问较低级别内存层次的技术。虽然绕过可以帮助减少缓存污染并提高内存访问的及时性,但是不同核心和内存分区之间的无协调绕过决策可能会导致性能下降。
这个领域的研究探索了技术以下是该 Markdown 文件的中文翻译。对于代码部分,我只翻译了注释,而没有翻译代码本身。
用于在 GPU 上协调缓存旁路决策,以提高整体系统性能和资源利用率。
例如,Li 等人 [2015] 提出了一种用于 GPU 的协调缓存旁路方案,该方案使用集中式旁路控制器做出全局旁路决策。控制器收集每个核心的运行时信息,如缓存失误率和内存访问模式,并使用这些信息来确定每个核心的最佳旁路策略。作者表明,与不协调的旁路相比,他们的方案可以显著提高性能和能源效率。
自适应缓存管理
GPU 应用程序的最佳缓存配置可能会根据其内存访问模式、工作集大小和资源需求而显著变化。在设计时固定的静态缓存管理策略可能无法适应不同应用程序的多样化和动态行为。
这个领域的研究探索了基于应用程序运行时行为动态调整缓存配置和管理策略的技术。
例如,Wang 等人 [2016] 提出了一种用于 GPU 的自适应缓存管理方案,该方案根据应用程序的内存访问模式动态调整缓存分区大小和替换策略。该方案使用硬件和软件技术的结合来监控缓存行为,并进行动态调整以提高缓存利用率和性能。
另一个例子是 Dai 等人 [2018] 的工作,他们提出了一种基于机器学习的自适应缓存管理方法for GPU。作者使用强化学习自动学习每个应用程序基于其运行时行为的最佳缓存配置。然后,学习到的策略使用可重配置的缓存架构实现,该架构可以适应每个应用程序的特定需求。
缓存优先级
在 GPU 中,不同类型的内存请求,如加载、存储和纹理请求,可能有不同的延迟和带宽要求。优先处理某些类型的请求可以帮助提高整体系统性能和资源利用率。
这个领域的研究探索了在 GPU 缓存层次结构中优先处理不同类型内存请求的技术。
例如,Zhao 等人 [2018] 提出了一种 GPU 缓存优先级方案,根据内存请求的关键性和延迟敏感性,为不同类型的内存请求分配不同的优先级。该方案使用静态和动态信息的组合,如指令类型和依赖指令的数量,来确定每个请求的优先级。作者表明,与没有优先级的基线 GPU 相比,他们的方案可以显著提高性能和能源效率。
虚拟内存页面放置
GPU 传统上依赖于手动内存管理,程序员负责显式分配和释放内存。然而,最近的 GPU 开始支持虚拟内存,允许操作系统自动管理内存分配和放置。
这个领域的研究探索了优化 GPU 虚拟内存页面放置的技术,以提高内存访问局部性并减少地址转换开销。
例如,Zheng 等人 [2016] 提出了一种 GPU 页面放置方案,旨在通过将频繁访问在一起的页面放置在同一内存通道或银行来提高内存访问局部性。该方案使用硬件和软件技术的组合来监控应用程序的内存访问模式,并做出动态页面放置决策。
另一个例子是 Ganguly 等人 [2019] 的工作,提出了一种针对 GPU 的虚拟内存管理方案,旨在减少地址转换开销。该方案使用硬件和软件技术的组合。以下是该 Markdown 文件的中文翻译。对于代码部分,我只翻译了注释,而没有翻译代码本身。
数据放置
GPU 内存层次结构中数据的放置可能会对内存访问局部性和性能产生重大影响。优化数据放置可以帮助减少内存延迟,提高缓存利用率,并增加内存带宽利用率。
这个领域的研究探索了基于应用程序的内存访问模式和资源需求来优化 GPU 中数据放置的技术。
例如,Agarwal 等人 [2015] 提出了一种 GPU 数据放置方案,旨在通过将经常一起访问的数据放置在同一内存通道或银行来提高内存访问局部性。该方案结合了静态和动态分析来确定每个应用程序的最佳数据放置。
另一个例子是 Tang 等人 [2017] 的工作,提出了一种 GPU 数据放置方案,旨在通过根据访问模式将数据放置在不同的内存通道来提高内存带宽利用率。该方案使用基于机器学习的方法来预测应用程序的内存访问模式,并做出动态数据放置决策。
多芯片模块 GPU
随着 GPU 的性能和功耗需求不断增加,传统的单芯片设计可能无法满足需求。多芯片模块 (MCM) 设计,即将多个 GPU 芯片集成到单个封装中,已经成为解决这一问题的一个有前景的解决方案。
这个领域的研究探索了 MCM GPU 的设计和优化,包括内存系统架构、互连设计和资源管理。
例如,Arunkumar 等人 [2017] 提出了一种 MCM GPU 设计,使用高带宽、低延迟的互连来连接多个 GPU 芯片。作者还提出了一种内存系统架构,利用以下是该 Markdown 文件的中文翻译。对于代码部分,我只翻译了注释,而没有翻译代码本身。
利用 MCM 设计提高性能和能源效率。
另一个例子是 Milic 等人 [2018] 提出的一种用于 MCM GPU 的资源管理方案,旨在提高资源利用率并减少芯片间通信开销。该方案结合硬件和软件技术来监控应用程序的资源使用和通信模式,并做出动态资源分配决策。
结论
内存系统是现代 GPU 架构的关键组成部分,其设计和优化可能对整体系统性能和效率产生重大影响。随着并行工作负载的需求不断增长,研究人员正在探索各种技术来提高 GPU 内存系统的性能、可扩展性和适应性。
这一领域的一些关键研究方向包括内存访问调度和互连设计、缓存有效性、内存请求优先级和缓存旁路、利用 warp 间异质性、协调的缓存旁路、自适应缓存管理、缓存优先级、虚拟内存页面放置、数据放置以及多芯片模块设计。
通过探索这些和其他技术,研究人员旨在开发能够满足并行工作负载不断增长需求的 GPU 内存系统,同时保持高性能和能源效率。随着 GPU 不断发展并在机器学习、科学计算和数据分析等领域找到新的应用,内存系统的设计和优化将继续成为研究和创新的重要领域。