原文
MGPUSim: Enabling Multi-GPU Performance Modeling and Optimization
(Sun Y, Baruah T, Mojumder S A, et al., ISCA '19)
背景
- 单个GPU能支持的计算吞吐量大约为12.4 TFlops~14.7 TFlops,不足以支持未来数据中心和科学应用的处理需求
- 多GPU系统的性能受到CPU-to-GPU和GPU-to-GPU同步性的限制,并受到多GPU内存管理开销的限制。因此设计一个高效的内存管理系统和跨GPU通信结构是当前需要解决的问题
- 为了支持下一代多GPU平台的设计空间探索,需要快速的和准确的仿真工具和框架
已有GPU仿真器存在的问题
- 仿真已经过时的GPU架构。新的GPU增加了一些特征,例如系统级原子和GPUDirect以方便跨多个GPU的协同执行;(AMD GEM5 APU仿真器已经解决该问题)
- 缺乏模块性,使得对多GPU平台的仿真和配置是一项繁琐的任务
- 仿真速度慢
贡献
- MGPUSim:并行的多GPU架构仿真器,能提供高保真性、高灵活性和高性能
- AMD GCN3 ISA
- 高灵活性/可配置性
使用户可以方便的使用不同指令调度算法、内存层次结构设计和系统中的GPU数量对平台进行建模
- 易扩展性
在不明显改变仿真器本身的情况下,能在仿真器上添加新的特征
- 多线程能力
提供高效的功能仿真和详细的时序仿真,提高仿真速度
- 局部性API:在没有内核(为单GPU实现编写的)修改的多GPU平台上允许显式数据和计算布局控制的API扩展
- 渐进式分页迁移(Progressive Page Splitting Migration, PASI):允许多个GPU在运行时逐步完善数据布局的一种硬件方法
上述2个设计的目的: 1. 展示MGPUSim的功能,以及它是如何直接的对新的多GPU平台进行建模的 2. 提出并验证基于软件和基于硬件的解决方案可以提高多GPU平台的可扩展性
理论背景
GPU执行模型
工作过程:
- 传统:运行在CPU上的主机程序将数据复制到GPU内存中,并在GPU上执行GPU程序(内核);然后CPU将计算结果从GPU内存复制回系统内存
- 新的:GPU更加自主
- 通过内核队列,GPU可以在不借助CPU的情况下自主启动内核
- 统一内核和按需分页使GPU能够直接读写系统内存,而无需在CPU和GPU设备之间进行显式的数据移动。
- GPUDirect允许GPU读写位于另一个GPU上的内存。
- 新的技术还包括:允许GPU向CPU请求服务,执行系统调用,发起网络通信,在远程设备上执行任务…
- 新的特性都需要ISA和微体系结构的新的支持
多GPU平台
两类
- 离散多GPU模型
- 优点:提供最大的灵活性
- 缺点:无法利用单GPU应用来使用多GPU平台
- 统一多GPU模型:将多GPU隐藏在单个GPU接口后面
- 优点:更好编程
- 缺点:高延迟GPU内部通信,无法扩展的性能
本文:支持以上两类模型,用户可选择使用。
仿真器设计需求
1. 不需要修改即可实现扩展
2. 避免组件之间直接进行数据传输
- 需要考虑实际的通信开销
- 保证模块化
3. 具有时序性的跟踪数据
- 使用真实的数据值来表示访问时间
4. 并行的仿真并行硬件
- 使用多线程仿真器更新组件的状态
5. 避免不必要的状态更新
并行GPU仿真
两种常见的方法
- 保守仿真:不打断事件的时间顺序,需要在每个周期结束后进行全局同步(MGPUSim选择这种方式)
- 乐观仿真:支持对事件进行重排序,可以避免频繁的同步,减少仿真时间,以损失仿真的真实性为代价
MGPUSIM
Go编程语言:
1. 合理的性能
2. 易编程
3. 为多线程编程提供了原语级支持,允许衍生出大量Goroutines(轻量级线程),以较低的开销处理事件
仿真器框架
采用一种领域无关的设计方法,使得该框架可以被用于仿真各种组件,例如不同的GPU模型、CPU或加速器设备。
- 事件驱动的仿真器引擎
- 将组件的状态更新定义为事件
- 为整个仿真维护一个事件队列,并按照时间顺序触发事件
- 组件
- 在多GPU平台中的每个实体都是组件,例如GPU、CU和cache模块等
- 连接
- 组件之间只能使用请求通过连接来通信(迫使开发者显式的声明组件之间的协议)
- 也被用于建模芯片内和芯片间的互连网络
- Hooks(挂载)
- 是一小段软件,可以附着在仿真器上,用来读取仿真器的状态或更新仿真器的状态
- 事件驱动仿真器引擎、所有的组件和连接都是可挂载的
- 可执行非关键任务,例如收集执行轨迹(execution traces),记录调试信息,计算性能指标,记录阻塞原因,注入故障(对可靠性的研究)等。
- 保守的并行事件驱动策略
GPU模型
- Command Processor(CP)
- 负责与GPU驱动通信,并在ACE的帮助下启动内核
- 异步计算引擎(Asynchronous Compute Engine,ACE)
- 调度内核的波前在CU上运行
- 计算单元(Compute Unit,CU)
- cache
- 内存控制器
- write-through cache
- write-around cache
- write-back cache
- 内存控制器
- TLB
- 协作完成在L1 cache中虚拟地址转化为物理地址
- 远程直接内存访问(RDMA)
- 管理GPU间的通信
CU模型
- 调度器scheduler
- 取指仲裁器(fetch arbiter)
- 出指仲裁器(issue arbiter)
- 分别决定取值和出指的波前
- 解码器decoder
- 需要1个周期对指令解码,然后将指令送到执行单元
- 执行单元
- 流水设计
- 包括:读、执行、写
- 存储单元
- 标量寄存器文件(SGPR)
- 向量寄存器文件(VGPR)
- 本地数据共享(LDS)存储
仿真器API
两种运行模式
- native
- 提供OpenCL运行库的一个定制实现(C语言)
- 用户可以将MGPUSim提供的OpenCL库链接到工作负载的可执行程序,这样定制的OpenCL库可以重定向到MGPUSim的API调用,并在模拟的GPU上运行GPU内核。
- Go
- 允许用户用Go语言写一个主程序来定义内存布局并运行内核
- GPU驱动器提供一系列的类OpenCL的API以允许负载控制被仿真的GPU
- 负载执行过程
- 调用init函数以为接下来的API调用创建一个执行内容
- 然后,工作负载可以调用设备发现函数,并使用SelectGPU函数指定要使用的GPU。
- 最后,通过使用内存分配、内存拷贝和内核启动API可以实现工作负载的主体。
实验方法
两组实验
- 验证
- 设计研究的评估
仿真配置
- 多GPU平台配置
- 2 Intel Xeon E2560 v4 CPUs
- 2 AMD R9 Nano GPUs
- CPU和GPU通过一条16GB/s PCIe 3.0互连线连接
- 操作系统:Linux Ubuntu 16.04.4
- 软件堆栈:Radeon Open Compute Platform (ROCm) 1.7 software stack
- 以最高频率运行GPU
- 避免动态电压和频率缩放(DVFS)的影响
- 内核通过官方ROCm编译器编译
- 使用Radeon Compute Profiler进行时间结果的收集
- Go模式
- 使用执行时间作为验证指标
验证仿真器速度和多线程可扩展性:4核Intel core i7-4770 CPU
微基准测试集
- ALU-focused
- ALU操作指令
- 验证指令调度、指令流水线、指令缓存等
- L1 access-focused
- 对相同的地址进行内存读操作
- 测量cache延迟
- DRAM access-focused
- 使用64字节的步长对GPU DRAM进行重复访问
- 测量DRAM延迟
- L2 access-focused
- 首先读取1MB内存块中的每条缓存行,将整个1MB加载到L2 cache
- 测量L2 cache延迟
完整基准测试集
测试集:覆盖GPU内部内存访问模式
- 验证实验:将工作负载复制到两块GPU上运行
- 设计研究:将单个工作负载分配到多个单独的GPU上运行
- 多GPU协同工作
仿真器验证实验
- 微基准验证:验证MGPUSim仿真单颗GPU功能和时序的准确性
- 仿真一个R9 Nano GPU
- 对于L1向量cache、L2 cache和DRAM,仿真结果和实际运行结果几乎重叠
- 对于ALU,有细小的偏差
- 原因:存在随机DRAM刷新
- 全基准验证:2 R9 Nano GPUs
- 平均5.5%的偏差
- 原因:一些未考虑到的GPU硬件细节
- 并行仿真性能:验证MGPUSim仿真速度
- 在4个CPU线程下,仿真速度达到27k条指令/秒(KIPS)
- Multi2Sim 5.0:1.6 KIPS
- GPGPU-sim:0.8 KIPS
设计研究1:局部性API
局部性API通过为统一的多GPU模型添加一个运行时间扩展,结合了统一多GPU模型和离散多GPU模型两种方法。
统一多GPU:简单的编程模型
离散多GPU:允许对数据和计算布局更精确的控制
API设计
设计依据:大部分常规GPU工作负载都具有规律的、可预测的内存访问模式。编程者可以利用特定算法的知识来确保大部分的内存访问发生在本地GPU上,避免GPU之间的通信。
包含3种API:
- 扩展的GPU Discovery API
- 允许主程序发现UGPU和每一个IGPU
- 假设每一个IGPU都关联着一块内存区域
- Memory Placement API
- 允许程序员显式的将一个范围的内存映射到一个IGPU
- Compute Placement API
- 允许程序员指定启动内核的IGPU ID和在子模块上执行的工作组列表。
- 扩展了现有的内核启动API
仿真器实现
- 离散多GPU模型
- 统一多GPU模型
- 带局部性API的多GPU平台
评估
对象
- 单GPU
- 单片多GPU
- 统一的4-GPU配置,无局部性API
- 统一的4-GPU配置,有局部性API(与分离的多GPU模型性能相同)
结果
- 执行时间
- GPU之间通信
设计研究2:PASI
目的:使GPU硬件可以自动改善任意工作负载的数据布局。
页面迁移
原有设计存在的问题:RDMA引擎放置在L1和L2 cache之间,这种直接访问cache(DCA)的设计迫使所有GPU间的通信数据包都有一个小于或等于缓存行大小(典型值为64B)的有效载荷,造成互连带宽和空间局部性的利用率很低。
改进:页面迁移设计
- 在每一个内存控制器中集成一个页面迁移控制器(PMC),PMC的内部页目录直接存储在GPU的DRAM中
- 只引入了很小的额外存储开销
- 可以扩大GPU间通信数据包的范围,类似于TLB
页面迁移存在的问题:由于多个GPU之间可能会共享相同的数据,单个页面可能会在GPU之间来回迁移。无法解决多次读和多次写两种内存访问模式。
解决办法:
- 多次读——cache-only内存架构
- 多次写——页面分割
cache-only内存架构(COMA)
- 针对“多个GPU多同一个数据”的情况
- 在内存一致性协议的支持下,允许一段数据驻留在多个位置
- 轻量级ESI内存一致性协议
- E:私有的
- S:共享的
- I:无效的
- 工作在页面的粒度上,仅仅管理由内存控制器组成的子系统,独立于cache系统
- 工作过程
页面分割
- 针对“错误的共享——不同的GPU写系统页面的不同部分”的情况
- 动态的方法:允许硬件在执行时发现最合适的页面大小
- 工作过程
- 开始与一个大的页面尺寸
- 当页面需要从状态E过渡到状态I或S时,将页面一分为二,只迁移请求页面的一半
- 继续向下拆分,直到达到最小的页面大小
- 当相邻的页面达到一个GPU时,IOMMU将它们合并成一个大页面
仿真器实现
- 页面迁移控制器(PMC)
- 3个端口
- L2CachePort
- DRAMPort
- RDMAPort
- 负责处理PMC和相连组件之间的通信
- 2种类型的请求
- PageMigrationReq
- PageMigrationRsp
评估
相关工作
- GPU仿真器
- GPGPU-Sim:NVIDIA’s PTX ISA
- Multi2Sim:AMD’s GCN1 ISA
- AMD GEM5 APU
- 并行GPU仿真器
- Barra:并行功能评估
- GPUTejas:基于Java,trace-driven
- GPGPU-Sim改进(Lee et at.):只有当处理器访问内存系统时才进行同步
- MAFIA:可以在GPGPU-Sim上运行并发内核的多线程仿真器,但单内核的仿真不能并行
- 多GPU微架构研究
- 优化内存组织结构
- 类似于cache的存储系统
- 局部性API
- locality descriptor:在单GPU和多GPU环境中减少内存移动
- 本文的局部性API只减少GPU之间的数据移动
- GPU中的虚拟地址管理