1. 概述
前文已经给出了aperture的定义。在 AMD GPU 的 HSA运行时中,Aperture(孔径)是一个核心概念,用于管理和组织不同类型的内存区域。libhsakmt 库通过精心设计的 aperture 机制,实现了 CPU 和 GPU 之间高效、灵活的内存管理。本文档详细阐述 libhsakmt 中 aperture 的类型、设计原理和使用场景。
2. Aperture 的核心概念
2.1 什么是 Aperture
Aperture 本质上是虚拟地址空间的一个连续区域,具有特定的起始地址和结束地址。每个 aperture 管理特定类型的内存,并提供该内存的分配、释放和映射功能。通过 aperture,系统可以将不同属性的内存隔离管理,确保内存访问的正确性和效率。
2.2 基本数据结构
aperture_t(简单 aperture):
- base:aperture 的起始地址
- limit:aperture 的结束地址
- 只记录地址范围,不进行主动管理
manageable_aperture_t(可管理 aperture):
- 继承 aperture_t 的 base 和 limit
- align:内存对齐要求
- guard_pages:保护页数量,用于检测越界访问
- vm_ranges:空闲地址区域链表
- tree 和 user_tree:红黑树,用于快速查找已分配的内存对象
- fmm_mutex:线程同步互斥锁
- is_cpu_accessible:标识 CPU 是否可直接访问
- ops:操作函数指针,支持不同的内存管理策略
3. Aperture 类型详解
3.1 SVM Aperture(共享虚拟内存孔径)
SVM aperture 是最重要的内存区域,用于实现 CPU 和 GPU 之间的统一地址空间。
设计目标:
- 实现 CPU 和 GPU 共享同一虚拟地址,简化编程模型
- 支持细粒度和粗粒度两种内存一致性模型
具体实现:
-
双 aperture 设计
- SVM_DEFAULT:用于粗粒度(非一致性)内存,占据 SVM 空间的后 3/4
- SVM_COHERENT:用于细粒度(一致性)内存,占据 SVM 空间的前 1/4
-
地址空间分配策略
- 优先尝试使用未预留的虚拟地址空间(通过 mmap_aperture_ops)
- 如果失败,则预留固定的地址空间(通过 reserved_aperture_ops)
- 支持最大 47 位地址空间(128TB)
-
适用场景:
- dGPU(独立显卡)的主要内存管理区域
- 支持 HSA 统一内存模型的应用
- 需要 CPU-GPU 零拷贝数据共享的场景
3.2 GPUVM Aperture(GPU 虚拟内存孔径)
GPUVM aperture 用于 APU(集成显卡)环境,管理非规范地址空间的 GPU 内存。
设计特点:
- 仅在非规范地址模式下存在(地址 < 2^47)
- 每个 GPU 拥有独立的 GPUVM aperture
- 在初始化时预留一小部分空间,避免空指针问题
使用场景:
- APU 系统的 GPU 显存管理
- 传统 GPU 内存分配(非 SVM 模式)
- 需要独立 GPU 地址空间的应用
3.3 Scratch Aperture(临时内存孔径)
Scratch aperture 是为 GPU 计算任务提供的临时工作空间。
功能特性:
- 每个 GPU 独立的 scratch 地址空间
- 用于存储 shader 程序的临时变量
- 按需分配,支持动态映射
物理实现:
- scratch_aperture:虚拟地址范围
- scratch_physical:对应的物理内存(从 dgpu_aperture 分配)
典型应用:
- GPU kernel 执行时的栈空间
- 线程局部存储(Thread Local Storage)
- 寄存器溢出时的临时存储
3.4 LDS Aperture(本地数据共享孔径)
LDS(Local Data Share)aperture 管理 GPU 工作组内共享的高速内存。
核心特征:
- 只读的虚拟地址范围(由内核驱动管理)
- 工作组内的所有工作项可共享访问
- 访问速度远快于全局内存
使用限制:
- 大小固定且有限(通常 64KB 每个计算单元)
- 生命周期仅限于工作组执行期间
- 不能跨工作组访问
3.5 MMIO Aperture(内存映射 I/O 孔径)
MMIO aperture 提供对 GPU 寄存器和特殊硬件资源的访问。
设计要点:
- 映射 GPU 的硬件寄存器到 CPU 可访问的地址空间
- 通常只有一个页面大小(4KB)
- 需要支持 SVM 的 GPU 才会创建
应用场景:
- Doorbell 寄存器访问(队列通知)
- 硬件计数器读取
- 调试和性能监控
3.6 CPUVM Aperture(CPU 虚拟内存孔径)
CPUVM aperture 用于追踪系统内存中不需要 GPU 驱动管理的部分。
特点:
- 管理 APU 上 GPU 不直接访问的系统内存
- 每个进程独立的追踪空间
- 使用 mmap 进行地址分配
使用情况:
- 用户态程序分配的普通内存
- 不需要 GPU 访问的缓冲区
- CPU 专用的数据结构
3.7 Memory Handle Aperture(内存句柄孔径)
Memory handle aperture 为没有有效虚拟地址的内存分配提供句柄空间。
设计目的:
- 为纯 GPU 内存(无 CPU 映射)生成标识符
- 地址范围位于非规范地址空间(47 位以上)
- 47 位地址空间大小
典型应用:
- 仅 GPU 可访问的显存(mflags.ui32.NoAddress)
- 跨进程内存共享的句柄
- 内存导出和导入操作
4. Aperture 管理机制
4.1 双操作策略
libhsakmt 为 aperture 设计了两种内存管理策略,通过函数指针实现多态:
reserved_aperture_ops(预留式管理):
- 预先通过 mmap 预留整块地址空间
- 使用链表管理空闲区域
- 分配时从空闲链表中查找合适的"洞"
- 适用于地址范围固定且可控的场景
这是老的实现,在新硬件上基本不用了。而是用下面的mmap_aperture_ops实现。
mmap_aperture_ops(按需映射):
- 不预留地址空间,按需调用 mmap
- 依赖操作系统的地址分配
- 更灵活但可能碎片化
- 适用于 48 位地址空间的现代 GPU
4.2 内存对象追踪
每个分配的内存都关联一个 vm_object_t 结构:
- start:内存起始地址
- size:实际分配大小
- handles:KFD 驱动句柄数组
- mflags:内存属性标志
- registered_device_id_array:注册的设备列表
- mapped_device_id_array:已映射的设备列表
通过红黑树(rbtree)实现快速查找:
- tree:按虚拟地址索引
- user_tree:按用户指针索引(用于 userptr 注册)
4.3 内存对齐优化
libhsakmt 实现了智能的内存对齐策略:
- 基础对齐:至少满足 aperture 的 align 要求
- 大小自适应对齐:大缓冲区向上对齐到下一个 2 的幂次,最大到 GPU_HUGE_PAGE_SIZE
- TLB 优化:通过对齐减少 TLB 缺失,提升访问性能
- 保护页:每个对象后添加 guard pages,检测越界访问
4.4 NUMA 感知
在系统内存分配时,libhsakmt 会:
- 识别 GPU 的 NUMA 节点
- 使用 mbind 将内存绑定到最近的 CPU 节点
- 减少跨 NUMA 访问延迟
- 支持 NoSubstitute 标志强制绑定
5. 初始化流程
5.1 整体初始化
hsakmt_fmm_init_process_apertures() 是 aperture 系统的入口:
-
解析环境变量
- HSA_DISABLE_CACHE:禁用 GPU 缓存
- HSA_USERPTR_FOR_PAGED_MEM:使用 userptr 管理分页内存
- HSA_SVM_GUARD_PAGES:设置保护页数量
-
创建 GPU 内存管理结构
- 为每个 GPU 节点分配 gpu_mem_t
- 打开 DRM render 设备文件
- 初始化 aperture 的互斥锁
-
从内核获取 aperture 信息
- 调用 AMDKFD_IOC_GET_PROCESS_APERTURES
- 获取每个 GPU 的 LDS、Scratch、GPUVM 地址范围
-
初始化 SVM aperture
- 计算所有 GPU 的公共地址空间
- 预留或映射 SVM 区域
- 设置内存策略(一致性/非一致性)
-
创建辅助 aperture
- CPUVM aperture:追踪系统内存
- Memory handle aperture:生成内存句柄
5.2 SVM Aperture 初始化策略
现代 GPU(GFXv9+):
- 支持完整 47 位地址空间
- 优先使用 mmap_aperture(按需分配)
- 测试分配一页内存,成功则使用该策略
传统 GPU 或受限场景:
- 预留固定的地址空间(最大 40 位,1TB)
- 使用多轮尝试,每次减半尝试大小
- 最小保证 4GB 可用空间
- 创建两个 aperture:1/4 为 coherent,3/4 为 default
6. 典型使用场景
6.1 分配设备内存
当应用请求分配 GPU 内存时:
- 根据 GPU ID 选择对应的 aperture(SVM 或 GPUVM)
- 从 aperture 中分配虚拟地址
- 调用 AMDKFD_IOC_ALLOC_MEMORY_OF_GPU 分配物理内存
- 创建 vm_object 追踪该内存
- 如需 CPU 访问,建立 mmap 映射
6.2 注册用户内存(Userptr)
用户可以将自己分配的内存注册到 GPU:
- 验证地址范围的有效性
- 在 SVM aperture 中创建 vm_object(使用 userptr 字段)
- 调用 KFD 驱动的 SVM API 或传统 userptr 注册
- 插入到 user_tree 中以便查找
6.3 内存映射到 GPU
内存分配后需要映射到特定 GPU:
- 查找内存对应的 vm_object
- 确定要映射的 GPU 列表(默认全部或指定部分)
- 调用 AMDKFD_IOC_MAP_MEMORY_TO_GPU
- 更新 mapped_device_id_array
- 增加映射计数器
7. 设计亮点
7.1 统一的内存模型
通过 SVM aperture,实现 CPU 和 GPU 使用相同的虚拟地址,消除了地址转换的复杂性。应用程序可以在 CPU 上分配内存,直接传递指针给 GPU 使用,无需额外的拷贝或映射操作。
7.2 灵活的策略切换
通过操作函数指针(ops),同一套代码可以支持预留式和按需式两种内存管理策略。系统根据 GPU 能力和运行环境自动选择最优策略,无需应用层感知。
7.3 细粒度的内存属性
每个 aperture 可以配置不同的属性:
- 内存一致性(CoarseGrain vs FineGrain)
- CPU 可访问性
- 对齐要求
- 保护页配置
7.4 高效的内存查找
使用红黑树实现 O(log n) 的内存对象查找,支持大规模内存分配场景。同时维护两棵树(按地址和按 userptr),满足不同的查询需求。
7.5 NUMA 优化
自动识别 GPU 的 NUMA 拓扑,将内存绑定到最近的 CPU 节点,最小化跨节点访问延迟,提升整体系统性能。
8. 与 HSA 标准的对应
libhsakmt 的 aperture 设计完整实现了 HSA 规范的内存模型:
- Global Memory:通过 SVM aperture 和 GPUVM aperture 实现
- Group Memory (LDS):通过 LDS aperture 暴露
- Flat Address Space:SVM aperture 提供统一的平坦地址空间
- Memory Regions:不同 aperture 对应不同的内存区域属性
9. 总结
9.1 Aperture 类型对比
下表总结了七种 aperture 的核心特征和差异:
| Aperture 类型 | 主要用途 | 地址空间范围 | 管理策略 | CPU 可访问 | GPU 可访问 | 适用场景 |
|---|---|---|---|---|---|---|
| SVM Aperture | CPU-GPU 统一地址空间 | 最大 47 位 (128TB) | reserved/mmap 自适应 | ✓ | ✓ | dGPU 主内存区域,零拷贝共享 |
| GPUVM Aperture | GPU 专用虚拟内存 | 非规范地址 (< 2^47) | reserved | × | ✓ | APU 显存,传统 GPU 内存 |
| Scratch Aperture | GPU 临时工作空间 | 每 GPU 独立范围 | reserved | 部分(调试) | ✓ | Kernel 栈,寄存器溢出 |
| LDS Aperture | 工作组共享内存 | 固定小范围 | 只读(内核管理) | × | ✓ | 工作组内高速共享 |
| MMIO Aperture | 硬件寄存器访问 | 单页 (4KB) | 直接映射 | ✓ | - | Doorbell,硬件计数器 |
| CPUVM Aperture | 系统内存追踪 | 47 位用户空间 | mmap | ✓ | × | 用户态普通内存 |
| Memory Handle Aperture | 内存句柄生成 | 非规范地址 (47 位) | reserved | × | × | 纯 GPU 内存标识符 |
9.2 Aperture 特性对比
不同 aperture 的管理特性对比:
| 特性 | SVM | GPUVM | Scratch | LDS | MMIO | CPUVM | Mem Handle |
|---|---|---|---|---|---|---|---|
| 可分配内存 | ✓ | ✓ | ✓ | × | ✓ | × | ✓ |
| 支持 mmap | ✓ | × | ✓ | × | ✓ | ✓ | × |
| 红黑树追踪 | ✓ | ✓ | ✓ | × | × | ✓ | ✓ |
| NUMA 绑定 | ✓ | × | ✓ | × | × | ✓ | × |
| 保护页机制 | ✓ | ✓ | ✓ | × | × | ✓ | ✓ |
| 动态映射 | ✓ | ✓ | ✓ | × | × | × | × |
| 跨进程共享 | ✓ | ✓ | × | × | × | × | ✓ |
9.3 使用场景决策树
选择合适的 aperture 类型:
分配内存需求
├─ 需要 CPU-GPU 零拷贝共享?
│ └─ 是 → SVM Aperture (dGPU) 或 GPUVM Aperture (APU)
│
├─ 仅供 GPU kernel 临时使用?
│ └─ 是 → Scratch Aperture
│
├─ 工作组内共享?
│ └─ 是 → LDS Aperture
│
├─ 访问 GPU 硬件寄存器?
│ └─ 是 → MMIO Aperture
│
├─ 追踪用户态系统内存?
│ └─ 是 → CPUVM Aperture
│
└─ 纯 GPU 内存无 CPU 映射?
└─ 是 → Memory Handle Aperture
理解libhsakmt中设计的各种aperture是理解后续memory相关实现的前提,因为memory的alloc/free、map/unmap、register/deregister、share/unshare的都涉及到选择合适的aperture来操作。
2277

被折叠的 条评论
为什么被折叠?



