本章深入探讨HSA Runtime的内存分配与释放机制。在第13章了解了MemoryRegion的基本概念后,本章将详细介绍内存分配的完整流程、对齐要求、大页支持、VRAM管理策略,以及如何通过Driver层与内核驱动交互完成实际的内存操作。
难度级别 : 🟡 中级
预计阅读时间 : 55分钟
前置知识: 第02章(架构概览)、第09章(Driver层)、第13章(Memory Region)
📋 本章学习目标
完成本章学习后,你将能够:
- ✅ 理解HSA内存分配的完整调用链(从API到Driver到KFD)
- ✅ 掌握System Memory 和 VRAM 的不同分配策略
- ✅ 学会用
hsa_amd_pointer_info反查某次分配的运行时状态 - ✅ 了解内存对齐要求和大页支持机制
- ✅ 熟悉Driver层在内存管理中的关键作用
- ✅ 理解VRAM管理的策略和碎片处理
- ✅ 学会诊断和优化内存分配性能问题
14.1 内存分配完整流程
14.1.1 从API到内核的完整路径
HSA内存分配涉及多个层次的协作,让我们先看完整的调用链:

关键设计点:
- ✅ 分层职责:每层负责不同的逻辑(验证、策略、转换、执行)
- ✅ Driver中介:所有硬件操作必须通过Driver层
- ✅ 参数转换:Driver负责HSA类型到内核类型的转换
- ✅ 错误传播:错误码在各层间正确映射和传播
14.1.2 两种主要分配路径
根据内存类型的不同,分配路径有所差异:
路径1:System Memory分配(主机内存)
#mermaid-svg-dYML2L8yQMR0iJ60{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-dYML2L8yQMR0iJ60 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-dYML2L8yQMR0iJ60 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-dYML2L8yQMR0iJ60 .error-icon{fill:#552222;}#mermaid-svg-dYML2L8yQMR0iJ60 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-dYML2L8yQMR0iJ60 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-dYML2L8yQMR0iJ60 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-dYML2L8yQMR0iJ60 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-dYML2L8yQMR0iJ60 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-dYML2L8yQMR0iJ60 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-dYML2L8yQMR0iJ60 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-dYML2L8yQMR0iJ60 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-dYML2L8yQMR0iJ60 .marker.cross{stroke:#333333;}#mermaid-svg-dYML2L8yQMR0iJ60 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-dYML2L8yQMR0iJ60 p{margin:0;}#mermaid-svg-dYML2L8yQMR0iJ60 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-dYML2L8yQMR0iJ60 .cluster-label text{fill:#333;}#mermaid-svg-dYML2L8yQMR0iJ60 .cluster-label span{color:#333;}#mermaid-svg-dYML2L8yQMR0iJ60 .cluster-label span p{background-color:transparent;}#mermaid-svg-dYML2L8yQMR0iJ60 .label text,#mermaid-svg-dYML2L8yQMR0iJ60 span{fill:#333;color:#333;}#mermaid-svg-dYML2L8yQMR0iJ60 .node rect,#mermaid-svg-dYML2L8yQMR0iJ60 .node circle,#mermaid-svg-dYML2L8yQMR0iJ60 .node ellipse,#mermaid-svg-dYML2L8yQMR0iJ60 .node polygon,#mermaid-svg-dYML2L8yQMR0iJ60 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-dYML2L8yQMR0iJ60 .rough-node .label text,#mermaid-svg-dYML2L8yQMR0iJ60 .node .label text,#mermaid-svg-dYML2L8yQMR0iJ60 .image-shape .label,#mermaid-svg-dYML2L8yQMR0iJ60 .icon-shape .label{text-anchor:middle;}#mermaid-svg-dYML2L8yQMR0iJ60 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-dYML2L8yQMR0iJ60 .rough-node .label,#mermaid-svg-dYML2L8yQMR0iJ60 .node .label,#mermaid-svg-dYML2L8yQMR0iJ60 .image-shape .label,#mermaid-svg-dYML2L8yQMR0iJ60 .icon-shape .label{text-align:center;}#mermaid-svg-dYML2L8yQMR0iJ60 .node.clickable{cursor:pointer;}#mermaid-svg-dYML2L8yQMR0iJ60 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-dYML2L8yQMR0iJ60 .arrowheadPath{fill:#333333;}#mermaid-svg-dYML2L8yQMR0iJ60 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-dYML2L8yQMR0iJ60 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-dYML2L8yQMR0iJ60 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-dYML2L8yQMR0iJ60 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-dYML2L8yQMR0iJ60 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-dYML2L8yQMR0iJ60 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-dYML2L8yQMR0iJ60 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-dYML2L8yQMR0iJ60 .cluster text{fill:#333;}#mermaid-svg-dYML2L8yQMR0iJ60 .cluster span{color:#333;}#mermaid-svg-dYML2L8yQMR0iJ60 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-dYML2L8yQMR0iJ60 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-dYML2L8yQMR0iJ60 rect.text{fill:none;stroke-width:0;}#mermaid-svg-dYML2L8yQMR0iJ60 .icon-shape,#mermaid-svg-dYML2L8yQMR0iJ60 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-dYML2L8yQMR0iJ60 .icon-shape p,#mermaid-svg-dYML2L8yQMR0iJ60 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-dYML2L8yQMR0iJ60 .icon-shape .label rect,#mermaid-svg-dYML2L8yQMR0iJ60 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-dYML2L8yQMR0iJ60 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-dYML2L8yQMR0iJ60 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-dYML2L8yQMR0iJ60 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} MemoryRegion (System, Fine-grained)
Agent->driver_->AllocMemory(node_id, size,
HSA_MEM_HEAP_TYPE_SYSTEM | HSA_MEM_FLAG_COHERENT, &ptr)
KfdDriver::AllocMemory()
• hsaKmtAllocMemory()
• 内核分配 DDR 内存
• 建立 CPU 页表映射
• 映射到 GPU 地址空间(GPU 可访问)
路径2:VRAM分配(GPU显存)
#mermaid-svg-7B0ViwBNgThTuxiP{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-7B0ViwBNgThTuxiP .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-7B0ViwBNgThTuxiP .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-7B0ViwBNgThTuxiP .error-icon{fill:#552222;}#mermaid-svg-7B0ViwBNgThTuxiP .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-7B0ViwBNgThTuxiP .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-7B0ViwBNgThTuxiP .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-7B0ViwBNgThTuxiP .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-7B0ViwBNgThTuxiP .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-7B0ViwBNgThTuxiP .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-7B0ViwBNgThTuxiP .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-7B0ViwBNgThTuxiP .marker{fill:#333333;stroke:#333333;}#mermaid-svg-7B0ViwBNgThTuxiP .marker.cross{stroke:#333333;}#mermaid-svg-7B0ViwBNgThTuxiP svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-7B0ViwBNgThTuxiP p{margin:0;}#mermaid-svg-7B0ViwBNgThTuxiP .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-7B0ViwBNgThTuxiP .cluster-label text{fill:#333;}#mermaid-svg-7B0ViwBNgThTuxiP .cluster-label span{color:#333;}#mermaid-svg-7B0ViwBNgThTuxiP .cluster-label span p{background-color:transparent;}#mermaid-svg-7B0ViwBNgThTuxiP .label text,#mermaid-svg-7B0ViwBNgThTuxiP span{fill:#333;color:#333;}#mermaid-svg-7B0ViwBNgThTuxiP .node rect,#mermaid-svg-7B0ViwBNgThTuxiP .node circle,#mermaid-svg-7B0ViwBNgThTuxiP .node ellipse,#mermaid-svg-7B0ViwBNgThTuxiP .node polygon,#mermaid-svg-7B0ViwBNgThTuxiP .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-7B0ViwBNgThTuxiP .rough-node .label text,#mermaid-svg-7B0ViwBNgThTuxiP .node .label text,#mermaid-svg-7B0ViwBNgThTuxiP .image-shape .label,#mermaid-svg-7B0ViwBNgThTuxiP .icon-shape .label{text-anchor:middle;}#mermaid-svg-7B0ViwBNgThTuxiP .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-7B0ViwBNgThTuxiP .rough-node .label,#mermaid-svg-7B0ViwBNgThTuxiP .node .label,#mermaid-svg-7B0ViwBNgThTuxiP .image-shape .label,#mermaid-svg-7B0ViwBNgThTuxiP .icon-shape .label{text-align:center;}#mermaid-svg-7B0ViwBNgThTuxiP .node.clickable{cursor:pointer;}#mermaid-svg-7B0ViwBNgThTuxiP .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-7B0ViwBNgThTuxiP .arrowheadPath{fill:#333333;}#mermaid-svg-7B0ViwBNgThTuxiP .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-7B0ViwBNgThTuxiP .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-7B0ViwBNgThTuxiP .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-7B0ViwBNgThTuxiP .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-7B0ViwBNgThTuxiP .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-7B0ViwBNgThTuxiP .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-7B0ViwBNgThTuxiP .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-7B0ViwBNgThTuxiP .cluster text{fill:#333;}#mermaid-svg-7B0ViwBNgThTuxiP .cluster span{color:#333;}#mermaid-svg-7B0ViwBNgThTuxiP div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-7B0ViwBNgThTuxiP .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-7B0ViwBNgThTuxiP rect.text{fill:none;stroke-width:0;}#mermaid-svg-7B0ViwBNgThTuxiP .icon-shape,#mermaid-svg-7B0ViwBNgThTuxiP .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-7B0ViwBNgThTuxiP .icon-shape p,#mermaid-svg-7B0ViwBNgThTuxiP .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-7B0ViwBNgThTuxiP .icon-shape .label rect,#mermaid-svg-7B0ViwBNgThTuxiP .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-7B0ViwBNgThTuxiP .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-7B0ViwBNgThTuxiP .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-7B0ViwBNgThTuxiP :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} MemoryRegion (Local, Coarse-grained)
Agent->driver_->AllocMemory(node_id, size,
HSA_MEM_HEAP_TYPE_FB_PUBLIC | HSA_MEM_FLAG_PUBLIC, &ptr)
KfdDriver::AllocMemory()
• hsaKmtAllocMemory()
• 内核从 VRAM 分配
• 建立 GPU 页表
• (可选) 映射到 CPU 地址空间
关键区别:
| 特性 | System Memory | VRAM |
|---|---|---|
| 物理位置 | 主机DDR | GPU显存 |
| CPU访问 | 直接访问 | 需要映射(较慢) |
| GPU访问 | 通过PCIe | 高带宽 |
| 一致性 | 可选Fine-grained | 通常Coarse-grained |
| 容量 | 较大(GB级) | 有限(显卡配置) |
14.2 MemoryRegion的分配实现
14.2.1 MemoryRegion::Allocate核心逻辑
cpp
// core/runtime/amd_memory_region.cpp
namespace amd {
hsa_status_t MemoryRegion::Allocate(
size_t size,
AllocateFlags alloc_flags,
void** address) const {
// ==================== 1. 参数验证 ====================
if (size == 0 || address == nullptr) {
return HSA_STATUS_ERROR_INVALID_ARGUMENT;
}
// 检查是否超出单次分配上限
if (size > max_single_alloc_size_) {
return HSA_STATUS_ERROR_INVALID_ALLOCATION;
}
// 检查区域剩余容量
if (allocatable_size_ != 0) {
size_t allocated = GetAllocatedSize();
if (allocated + size > allocatable_size_) {
return HSA_STATUS_ERROR_OUT_OF_RESOURCES;
}
}
// ==================== 2. 对齐处理 ====================
size_t aligned_size = AlignUp(size, runtime_alloc_granule_);
// ==================== 3. 准备内存标志 ====================
HsaMemFlags flags = TranslateAllocateFlags(alloc_flags);
// ==================== 4. 调用Agent的分配接口(通过Driver)====================
hsa_status_t status = owner()->AllocateMemory(
aligned_size,
flags,
address);
if (status != HSA_STATUS_SUCCESS) {
return status;
}
// ==================== 5. 记录分配信息 ====================
{
ScopedLock lock(&allocation_map_lock_);
AllocationInfo info;
info.size = aligned_size;
info.region = this;
info.is_system = IsSystem();
allocation_map_[*address] = info;
}
return HSA_STATUS_SUCCESS;
}
} // namespace amd
关键步骤解析:
- 参数验证:检查大小、地址指针、容量限制
- 对齐处理:向上对齐到分配粒度(通常4KB或64KB)
- 标志转换:将HSA标志转换为Driver层需要的格式
- Driver调用:通过Agent的driver_成员调用实际分配
- 记录管理:维护分配映射表用于后续查询和释放
14.2.2 Agent层的分配实现
cpp
// core/runtime/amd_gpu_agent.cpp
namespace amd {
hsa_status_t GpuAgent::AllocateMemory(
size_t size,
HsaMemFlags flags,
void** mem) {
// Agent持有Driver引用(在构造时传入)
assert(driver_ != nullptr);
// 准备分配参数
uint32_t node_id = node_id_; // GPU节点ID
// 直接调用Driver接口
// 这是Agent与硬件交互的唯一方式
hsa_status_t status = driver_->AllocMemory(
node_id,
size,
flags,
mem);
if (status == HSA_STATUS_SUCCESS) {
// 成功后的额外处理
// 例如:记录到GPU内存跟踪、更新统计信息等
TrackAllocation(*mem, size);
}
return status;
}
} // namespace amd
设计要点:
- Agent作为"用户态设备抽象",不直接操作硬件
- 所有硬件相关操作委托给Driver
- Agent负责记录和追踪,Driver负责执行
14.3 查询指针信息(Pointer Info)
第13章 13.1 提到过两个互补的抽象层面:
- MemoryRegion / Memory Pool 描述"内存资源本身"------这类内存在哪里、属于哪个 agent、能否分配、是否 fine-grained。
- Allocation / Pointer Info 描述"某一次具体分配"------这个指针来自哪个 pool、当前物理位置在哪、哪些 agent 能访问、是否注册到驱动。
前几节讲的是"如何分配",本节回答分配之后的一个常见问题:拿到一个指针,如何反查它的运行时状态? ROCr 通过 hsa_amd_pointer_info 提供这种能力。
14.3.1 hsa_amd_pointer_info API
cpp
hsa_status_t hsa_amd_pointer_info(
const void* ptr, // 待查询的地址(可以是 buffer 内部任意地址)
hsa_amd_pointer_info_t* info, // 输出:指针信息
void* (*alloc)(size_t), // 可选:为 accessible agent 列表分配内存
uint32_t* num_agents_accessible, // 输出:可访问该地址的 agent 数量
hsa_agent_t** accessible); // 输出:可访问 agent 列表(由 alloc 分配)
配套的还有一个设置/读取用户数据的接口,允许应用把自定义指针挂到这块 allocation 上:
cpp
hsa_status_t hsa_amd_pointer_info_set_userdata(
const void* ptr,
void* userdata);
14.3.2 hsa_amd_pointer_info_t 结构
cpp
typedef struct hsa_amd_pointer_info_s {
uint32_t size; // 结构体大小,用于 ABI 兼容
hsa_amd_pointer_type_t type; // 指针类型(见下)
void* agentBaseAddress; // 该 allocation 在 agent 侧的基址
void* hostBaseAddress; // 该 allocation 在 host 侧的基址
size_t sizeInBytes; // allocation 的大小
void* userData; // set_userdata 设置的自定义数据
hsa_agent_t agentOwner; // 拥有该 allocation 的 agent
hsa_amd_memory_pool_global_flag_t global_flags; // fine/coarse 等 pool 属性
} hsa_amd_pointer_info_t;
其中 type 的取值:
cpp
typedef enum {
HSA_EXT_POINTER_TYPE_UNKNOWN = 0, // 非 HSA 管理的内存(如普通 malloc)
HSA_EXT_POINTER_TYPE_HSA = 1, // 由 HSA runtime 分配的内存
HSA_EXT_POINTER_TYPE_LOCKED = 2, // 通过 lock 锁页的 host 内存
HSA_EXT_POINTER_TYPE_GRAPHICS = 3, // 来自图形栈(interop)的内存
HSA_EXT_POINTER_TYPE_IPC = 4 // 通过 IPC 共享的内存
} hsa_amd_pointer_type_t;
各字段的含义与用途:
| 字段 | 含义 | 典型用途 |
|---|---|---|
type |
内存的来源/管理方式 | 判断指针是否 HSA 管理、是否锁页、是否 IPC |
agentBaseAddress |
allocation 在设备侧的基址 | 计算偏移、校验越界 |
hostBaseAddress |
allocation 在主机侧的基址 | host 与 device 地址映射 |
sizeInBytes |
allocation 总大小 | 边界检查、拷贝长度推断 |
agentOwner |
拥有该内存的 agent | 反查内存属于哪个 CPU/GPU |
global_flags |
fine/coarse 等标志 | 判断是否需要显式同步 |
userData |
应用自定义数据 | 挂接上层元数据(如分配器句柄) |
注意 agentBaseAddress / hostBaseAddress 返回的是该 allocation 的基址 ,而不是你传入的 ptr。传入 buffer 内部任意地址,都能反查回它所属 allocation 的起点和大小------这正是它比自己维护 allocation map 更方便的地方。
14.3.3 使用示例
cpp
void inspect_pointer(void* ptr) {
hsa_amd_pointer_info_t info;
info.size = sizeof(info); // 必须设置,用于 ABI 版本协商
uint32_t num_accessible = 0;
hsa_agent_t* accessible = nullptr;
hsa_status_t status = hsa_amd_pointer_info(
ptr,
&info,
malloc, // 由 runtime 用它分配 accessible 列表
&num_accessible,
&accessible);
if (status != HSA_STATUS_SUCCESS) {
printf("pointer_info failed\n");
return;
}
switch (info.type) {
case HSA_EXT_POINTER_TYPE_HSA:
printf("HSA-managed allocation\n");
break;
case HSA_EXT_POINTER_TYPE_LOCKED:
printf("locked host memory\n");
break;
case HSA_EXT_POINTER_TYPE_IPC:
printf("IPC shared memory\n");
break;
case HSA_EXT_POINTER_TYPE_UNKNOWN:
printf("not managed by HSA\n");
break;
default:
break;
}
printf("base = %p, size = %zu\n",
info.agentBaseAddress, info.sizeInBytes);
printf("accessible by %u agents\n", num_accessible);
// accessible 列表由传入的 malloc 分配,使用完需自行释放
free(accessible);
}
关键点:
- 必须设置
info.size:runtime 依赖它做结构体版本协商,忘记设置会导致查询失败或字段错乱。 alloc回调的所有权 :accessible列表由你传入的分配函数分配,用完由调用方释放 (示例里传malloc,就用free)。- 可传内部地址 :
ptr不必是 allocation 起点,runtime 会反查所属 allocation。 - UNKNOWN 不代表错误 :普通
malloc/ 栈地址会返回UNKNOWN,这是正常结果,可用于判断"这个指针是不是 HSA 管理的"。
14.3.4 与 MemoryRegion 的关系
Pointer Info 与前面章节的分配路径是一体两面:
#mermaid-svg-PfD8J5lJWwZmJi87{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-PfD8J5lJWwZmJi87 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-PfD8J5lJWwZmJi87 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-PfD8J5lJWwZmJi87 .error-icon{fill:#552222;}#mermaid-svg-PfD8J5lJWwZmJi87 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-PfD8J5lJWwZmJi87 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-PfD8J5lJWwZmJi87 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-PfD8J5lJWwZmJi87 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-PfD8J5lJWwZmJi87 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-PfD8J5lJWwZmJi87 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-PfD8J5lJWwZmJi87 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-PfD8J5lJWwZmJi87 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-PfD8J5lJWwZmJi87 .marker.cross{stroke:#333333;}#mermaid-svg-PfD8J5lJWwZmJi87 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-PfD8J5lJWwZmJi87 p{margin:0;}#mermaid-svg-PfD8J5lJWwZmJi87 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-PfD8J5lJWwZmJi87 .cluster-label text{fill:#333;}#mermaid-svg-PfD8J5lJWwZmJi87 .cluster-label span{color:#333;}#mermaid-svg-PfD8J5lJWwZmJi87 .cluster-label span p{background-color:transparent;}#mermaid-svg-PfD8J5lJWwZmJi87 .label text,#mermaid-svg-PfD8J5lJWwZmJi87 span{fill:#333;color:#333;}#mermaid-svg-PfD8J5lJWwZmJi87 .node rect,#mermaid-svg-PfD8J5lJWwZmJi87 .node circle,#mermaid-svg-PfD8J5lJWwZmJi87 .node ellipse,#mermaid-svg-PfD8J5lJWwZmJi87 .node polygon,#mermaid-svg-PfD8J5lJWwZmJi87 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-PfD8J5lJWwZmJi87 .rough-node .label text,#mermaid-svg-PfD8J5lJWwZmJi87 .node .label text,#mermaid-svg-PfD8J5lJWwZmJi87 .image-shape .label,#mermaid-svg-PfD8J5lJWwZmJi87 .icon-shape .label{text-anchor:middle;}#mermaid-svg-PfD8J5lJWwZmJi87 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-PfD8J5lJWwZmJi87 .rough-node .label,#mermaid-svg-PfD8J5lJWwZmJi87 .node .label,#mermaid-svg-PfD8J5lJWwZmJi87 .image-shape .label,#mermaid-svg-PfD8J5lJWwZmJi87 .icon-shape .label{text-align:center;}#mermaid-svg-PfD8J5lJWwZmJi87 .node.clickable{cursor:pointer;}#mermaid-svg-PfD8J5lJWwZmJi87 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-PfD8J5lJWwZmJi87 .arrowheadPath{fill:#333333;}#mermaid-svg-PfD8J5lJWwZmJi87 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-PfD8J5lJWwZmJi87 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-PfD8J5lJWwZmJi87 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-PfD8J5lJWwZmJi87 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-PfD8J5lJWwZmJi87 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-PfD8J5lJWwZmJi87 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-PfD8J5lJWwZmJi87 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-PfD8J5lJWwZmJi87 .cluster text{fill:#333;}#mermaid-svg-PfD8J5lJWwZmJi87 .cluster span{color:#333;}#mermaid-svg-PfD8J5lJWwZmJi87 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-PfD8J5lJWwZmJi87 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-PfD8J5lJWwZmJi87 rect.text{fill:none;stroke-width:0;}#mermaid-svg-PfD8J5lJWwZmJi87 .icon-shape,#mermaid-svg-PfD8J5lJWwZmJi87 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-PfD8J5lJWwZmJi87 .icon-shape p,#mermaid-svg-PfD8J5lJWwZmJi87 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-PfD8J5lJWwZmJi87 .icon-shape .label rect,#mermaid-svg-PfD8J5lJWwZmJi87 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-PfD8J5lJWwZmJi87 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-PfD8J5lJWwZmJi87 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-PfD8J5lJWwZmJi87 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} hsa_memory_allocate /
hsa_amd_memory_pool_allocate
MemoryRegion::Allocate
(14.2 节)
记录 allocation_map_
返回 ptr
hsa_amd_pointer_info(ptr)
反查 owner / pool /
size / accessible agents
- 分配阶段(14.2):MemoryRegion 记录了每次 allocation 的大小、owner、pool 等信息。
- 查询阶段(本节):
hsa_amd_pointer_info就是把这些内部记录以标准 API 形式暴露出来。 info.agentOwner对应 MemoryRegion 的 owner,info.global_flags对应 pool 的 fine/coarse 属性,accessible列表对应GetAccessibleAgents()。
因此,Pointer Info 是调试内存问题(第 14.10 节)时的重要工具:当你怀疑一个指针类型不对、owner 不对、或某个 agent 访问不了时,第一步往往就是打印它的 pointer info。
14.4 Driver层的内存分配实现
14.4.1 KfdDriver::AllocMemory详解
Driver层是连接Runtime和内核的关键桥梁:
cpp
// core/runtime/kfd_driver.cpp
namespace core {
hsa_status_t KfdDriver::AllocMemory(
uint32_t node_id,
size_t size,
HsaMemFlags flags,
void** mem) {
// ==================== 1. 参数转换 ====================
// HSA类型 → Thunk层类型
HsaMemoryBuffer mem_buf;
mem_buf.SizeInBytes = size;
mem_buf.Flags = flags;
// 设置分配属性
if (flags.ui32.HostAccess) {
mem_buf.Flags.ui32.NonPaged = 1; // 不分页,常驻内存
}
// ==================== 2. 调用Thunk接口 ====================
HSAKMT_STATUS kmt_status = hsaKmtAllocMemory(
node_id, // GPU节点
size, // 大小
flags, // 标志
mem); // 返回地址
// ==================== 3. 错误码转换 ====================
if (kmt_status != HSAKMT_STATUS_SUCCESS) {
return MapKmtError(kmt_status);
}
// ==================== 4. GPU地址空间映射 ====================
// 如果是系统内存,需要映射到GPU地址空间
if (flags.ui32.MemoryType == HSA_MEM_HEAP_TYPE_SYSTEM) {
uint64_t device_address;
kmt_status = hsaKmtMapMemoryToGPU(
*mem,
size,
&device_address);
if (kmt_status != HSAKMT_STATUS_SUCCESS) {
// 映射失败,清理已分配的内存
hsaKmtFreeMemory(*mem, size);
return MapKmtError(kmt_status);
}
}
return HSA_STATUS_SUCCESS;
}
// 错误码映射函数
hsa_status_t KfdDriver::MapKmtError(HSAKMT_STATUS kmt_status) {
switch (kmt_status) {
case HSAKMT_STATUS_NO_MEMORY:
return HSA_STATUS_ERROR_OUT_OF_RESOURCES;
case HSAKMT_STATUS_INVALID_PARAMETER:
return HSA_STATUS_ERROR_INVALID_ARGUMENT;
case HSAKMT_STATUS_INVALID_NODE_UNIT:
return HSA_STATUS_ERROR_INVALID_AGENT;
default:
return HSA_STATUS_ERROR;
}
}
} // namespace core
Driver层职责总结:
| 职责 | 说明 | 示例 |
|---|---|---|
| 参数转换 | HSA类型 ↔ 内核类型 | HsaMemFlags → kfd_ioctl_alloc_memory_of_gpu_args |
| 接口封装 | C++面向对象 ↔ C函数 | AllocMemory() → hsaKmtAllocMemory() |
| 错误处理 | 错误码映射 | HSAKMT_STATUS → hsa_status_t |
| 资源管理 | 异常时清理 | 映射失败时释放已分配内存 |
| 状态维护 | 设备状态跟踪 | 记录已分配内存、更新可用容量 |
14.4.2 System Memory vs VRAM的Driver处理差异
System Memory 分配流程(需额外映射到 GPU):

VRAM 分配流程(CPU 映射可选):

14.5 内存对齐与粒度
14.5.1 对齐要求的来源
#mermaid-svg-gfYgrMOAGXEcugdp{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-gfYgrMOAGXEcugdp .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-gfYgrMOAGXEcugdp .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-gfYgrMOAGXEcugdp .error-icon{fill:#552222;}#mermaid-svg-gfYgrMOAGXEcugdp .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-gfYgrMOAGXEcugdp .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-gfYgrMOAGXEcugdp .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-gfYgrMOAGXEcugdp .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-gfYgrMOAGXEcugdp .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-gfYgrMOAGXEcugdp .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-gfYgrMOAGXEcugdp .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-gfYgrMOAGXEcugdp .marker{fill:#333333;stroke:#333333;}#mermaid-svg-gfYgrMOAGXEcugdp .marker.cross{stroke:#333333;}#mermaid-svg-gfYgrMOAGXEcugdp svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-gfYgrMOAGXEcugdp p{margin:0;}#mermaid-svg-gfYgrMOAGXEcugdp .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-gfYgrMOAGXEcugdp .cluster-label text{fill:#333;}#mermaid-svg-gfYgrMOAGXEcugdp .cluster-label span{color:#333;}#mermaid-svg-gfYgrMOAGXEcugdp .cluster-label span p{background-color:transparent;}#mermaid-svg-gfYgrMOAGXEcugdp .label text,#mermaid-svg-gfYgrMOAGXEcugdp span{fill:#333;color:#333;}#mermaid-svg-gfYgrMOAGXEcugdp .node rect,#mermaid-svg-gfYgrMOAGXEcugdp .node circle,#mermaid-svg-gfYgrMOAGXEcugdp .node ellipse,#mermaid-svg-gfYgrMOAGXEcugdp .node polygon,#mermaid-svg-gfYgrMOAGXEcugdp .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-gfYgrMOAGXEcugdp .rough-node .label text,#mermaid-svg-gfYgrMOAGXEcugdp .node .label text,#mermaid-svg-gfYgrMOAGXEcugdp .image-shape .label,#mermaid-svg-gfYgrMOAGXEcugdp .icon-shape .label{text-anchor:middle;}#mermaid-svg-gfYgrMOAGXEcugdp .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-gfYgrMOAGXEcugdp .rough-node .label,#mermaid-svg-gfYgrMOAGXEcugdp .node .label,#mermaid-svg-gfYgrMOAGXEcugdp .image-shape .label,#mermaid-svg-gfYgrMOAGXEcugdp .icon-shape .label{text-align:center;}#mermaid-svg-gfYgrMOAGXEcugdp .node.clickable{cursor:pointer;}#mermaid-svg-gfYgrMOAGXEcugdp .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-gfYgrMOAGXEcugdp .arrowheadPath{fill:#333333;}#mermaid-svg-gfYgrMOAGXEcugdp .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-gfYgrMOAGXEcugdp .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-gfYgrMOAGXEcugdp .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-gfYgrMOAGXEcugdp .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-gfYgrMOAGXEcugdp .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-gfYgrMOAGXEcugdp .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-gfYgrMOAGXEcugdp .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-gfYgrMOAGXEcugdp .cluster text{fill:#333;}#mermaid-svg-gfYgrMOAGXEcugdp .cluster span{color:#333;}#mermaid-svg-gfYgrMOAGXEcugdp div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-gfYgrMOAGXEcugdp .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-gfYgrMOAGXEcugdp rect.text{fill:none;stroke-width:0;}#mermaid-svg-gfYgrMOAGXEcugdp .icon-shape,#mermaid-svg-gfYgrMOAGXEcugdp .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-gfYgrMOAGXEcugdp .icon-shape p,#mermaid-svg-gfYgrMOAGXEcugdp .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-gfYgrMOAGXEcugdp .icon-shape .label rect,#mermaid-svg-gfYgrMOAGXEcugdp .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-gfYgrMOAGXEcugdp .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-gfYgrMOAGXEcugdp .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-gfYgrMOAGXEcugdp :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 1. CPU 页面对齐 (4KB)
• 操作系统虚拟内存管理基本单位
• 最小对齐要求
2. GPU 页面对齐 (64KB)
• GPU MMU 页表粒度
• VRAM 分配通常要求
3. Cache Line 对齐 (64B/128B)
• 避免 False Sharing
• 提高访问效率
4. DMA 传输对齐 (256B)
• SDMA 引擎要求
• 提高传输效率
14.5.2 MemoryRegion的分配粒度
每种MemoryRegion有不同的分配粒度:
cpp
// 不同内存区域的分配粒度示例
System Memory (Fine-grained):
runtime_alloc_granule_ = 4096 // 4KB (CPU页面)
max_single_alloc_size_ = SIZE_MAX // 无限制
VRAM (Coarse-grained):
runtime_alloc_granule_ = 65536 // 64KB (GPU页面)
max_single_alloc_size_ = vram_size - reserved
LDS (Local Data Share):
runtime_alloc_granule_ = 1024 // 1KB
max_single_alloc_size_ = 65536 // 64KB (硬件限制)
14.5.3 大页支持 (Huge Pages)
对于大内存分配,使用大页可以显著提升性能:
cpp
// Driver层判断是否使用大页
hsa_status_t KfdDriver::AllocMemory(...) {
// 大于2MB的分配考虑使用大页
if (size >= 2 * 1024 * 1024) {
mem_buf.Flags.ui32.PageSize = HSA_PAGE_SIZE_2MB;
// 尝试2MB大页分配
status = hsaKmtAllocMemory(...);
if (status == HSAKMT_STATUS_NO_MEMORY) {
// 大页分配失败,回退到4KB
mem_buf.Flags.ui32.PageSize = HSA_PAGE_SIZE_4KB;
status = hsaKmtAllocMemory(...);
}
}
return status;
}
大页优势:
- ✅ 减少TLB Miss
- ✅ 降低页表遍历开销
- ✅ 提高大数据集访问性能
注意事项:
- ⚠️ 系统需要预留大页池
- ⚠️ 可能增加内存碎片
- ⚠️ 不适合小内存分配
14.6 VRAM管理策略
14.6.1 VRAM资源特点
GPU显存是有限且昂贵的资源,需要精心管理:
#mermaid-svg-RYilP9xHgLjhP58w{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-RYilP9xHgLjhP58w .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-RYilP9xHgLjhP58w .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-RYilP9xHgLjhP58w .error-icon{fill:#552222;}#mermaid-svg-RYilP9xHgLjhP58w .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-RYilP9xHgLjhP58w .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-RYilP9xHgLjhP58w .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-RYilP9xHgLjhP58w .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-RYilP9xHgLjhP58w .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-RYilP9xHgLjhP58w .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-RYilP9xHgLjhP58w .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-RYilP9xHgLjhP58w .marker{fill:#333333;stroke:#333333;}#mermaid-svg-RYilP9xHgLjhP58w .marker.cross{stroke:#333333;}#mermaid-svg-RYilP9xHgLjhP58w svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-RYilP9xHgLjhP58w p{margin:0;}#mermaid-svg-RYilP9xHgLjhP58w .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-RYilP9xHgLjhP58w .cluster-label text{fill:#333;}#mermaid-svg-RYilP9xHgLjhP58w .cluster-label span{color:#333;}#mermaid-svg-RYilP9xHgLjhP58w .cluster-label span p{background-color:transparent;}#mermaid-svg-RYilP9xHgLjhP58w .label text,#mermaid-svg-RYilP9xHgLjhP58w span{fill:#333;color:#333;}#mermaid-svg-RYilP9xHgLjhP58w .node rect,#mermaid-svg-RYilP9xHgLjhP58w .node circle,#mermaid-svg-RYilP9xHgLjhP58w .node ellipse,#mermaid-svg-RYilP9xHgLjhP58w .node polygon,#mermaid-svg-RYilP9xHgLjhP58w .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-RYilP9xHgLjhP58w .rough-node .label text,#mermaid-svg-RYilP9xHgLjhP58w .node .label text,#mermaid-svg-RYilP9xHgLjhP58w .image-shape .label,#mermaid-svg-RYilP9xHgLjhP58w .icon-shape .label{text-anchor:middle;}#mermaid-svg-RYilP9xHgLjhP58w .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-RYilP9xHgLjhP58w .rough-node .label,#mermaid-svg-RYilP9xHgLjhP58w .node .label,#mermaid-svg-RYilP9xHgLjhP58w .image-shape .label,#mermaid-svg-RYilP9xHgLjhP58w .icon-shape .label{text-align:center;}#mermaid-svg-RYilP9xHgLjhP58w .node.clickable{cursor:pointer;}#mermaid-svg-RYilP9xHgLjhP58w .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-RYilP9xHgLjhP58w .arrowheadPath{fill:#333333;}#mermaid-svg-RYilP9xHgLjhP58w .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-RYilP9xHgLjhP58w .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-RYilP9xHgLjhP58w .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-RYilP9xHgLjhP58w .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-RYilP9xHgLjhP58w .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-RYilP9xHgLjhP58w .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-RYilP9xHgLjhP58w .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-RYilP9xHgLjhP58w .cluster text{fill:#333;}#mermaid-svg-RYilP9xHgLjhP58w .cluster span{color:#333;}#mermaid-svg-RYilP9xHgLjhP58w div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-RYilP9xHgLjhP58w .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-RYilP9xHgLjhP58w rect.text{fill:none;stroke-width:0;}#mermaid-svg-RYilP9xHgLjhP58w .icon-shape,#mermaid-svg-RYilP9xHgLjhP58w .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-RYilP9xHgLjhP58w .icon-shape p,#mermaid-svg-RYilP9xHgLjhP58w .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-RYilP9xHgLjhP58w .icon-shape .label rect,#mermaid-svg-RYilP9xHgLjhP58w .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-RYilP9xHgLjhP58w .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-RYilP9xHgLjhP58w .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-RYilP9xHgLjhP58w :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 优势
• 带宽极高 (1TB/s+)
• GPU 访问延迟低
• 适合频繁访问数据
限制
• 容量有限 (4GB ~ 128GB)
• CPU 访问慢 (通过 PCIe BAR)
• 不能扩展
14.6.2 Runtime的VRAM管理策略
cpp
// GpuAgent维护VRAM使用情况
class GpuAgent {
private:
size_t vram_total_; // 总VRAM容量
size_t vram_allocatable_; // 可分配容量(扣除reserved)
size_t vram_allocated_; // 已分配大小
// VRAM分配跟踪
struct VramAllocation {
void* address;
size_t size;
uint64_t timestamp;
};
std::map<void*, VramAllocation> vram_map_;
public:
// 检查是否有足够VRAM
bool HasAvailableVram(size_t size) const {
return (vram_allocated_ + size) <= vram_allocatable_;
}
// 获取VRAM使用率
float GetVramUsagePercentage() const {
return (float)vram_allocated_ / vram_allocatable_ * 100.0f;
}
};
14.6.3 VRAM耗尽时的处理策略
VRAM 不足的处理要分清 两个层次 :ROCr Runtime 用户态本身几乎不做数据搬迁,真正的"换出/驱逐"是内核 amdgpu/TTM 透明完成的。
ROCr Runtime(用户态)层:
- 直接失败返回 :这是 ROCr 的主要行为。
hsaKmtAllocMemory失败后,MemoryRegion::Allocate返回HSA_STATUS_ERROR_OUT_OF_RESOURCES,由应用程序自行决定回退(例如改用 System Memory)。 - ROCr 不会移动已分配缓冲区的虚拟地址,也不会在用户态实现换页或碎片整理------因为 GPU 虚拟地址一旦分配就固定(已写入内核参数、描述符),搬迁会破坏正在使用的指针。
内核 amdgpu / TTM 层(对应用透明):
- 驱逐 / Overcommit:当 VRAM 压力大时,TTM 会把暂时不用的 BO 从 VRAM 驱逐到 GTT / System Memory,需要时再迁回。这一切在内核完成,应用看到的 GPU 虚拟地址不变。
- KFD 迁移(SVM):对统一地址(SVM/HMM)内存,内核可在 VRAM 与系统内存之间迁移页面,同样对用户态透明。
换句话说:用户态 ROCr ≈ "分配失败就报错",内核才负责真正的换出与迁移。不要把"Swapping / 标记-清理迁移 / 碎片整理"当成 ROCr 用户态的行为。
实践建议:
由于没有可靠的"已用 VRAM"查询接口,推荐 "先尝试分配、失败再回退" 的模式,而不是先算余量:
cpp
// 查询内存池总量与单次最大可分配大小(用于容量规划)
size_t total_vram = 0, max_alloc = 0;
hsa_amd_memory_pool_get_info(
gpu_pool, HSA_AMD_MEMORY_POOL_INFO_SIZE, &total_vram);
hsa_amd_memory_pool_get_info(
gpu_pool, HSA_AMD_MEMORY_POOL_INFO_ALLOC_MAX_SIZE, &max_alloc);
// 推荐做法:直接尝试分配,捕获 OUT_OF_RESOURCES 后回退到 System Memory
void* ptr = nullptr;
hsa_status_t st =
hsa_amd_memory_pool_allocate(gpu_pool, required_size, 0, &ptr);
if (st == HSA_STATUS_ERROR_OUT_OF_RESOURCES) {
// VRAM 不足,回退到 System Memory
hsa_amd_memory_pool_allocate(system_pool, required_size, 0, &ptr);
}
注意:
HSA_AMD_MEMORY_POOL_INFO_ACCESSIBLE_BY_ALL返回的是一个布尔标志(该池是否对所有 Agent 可见),不是已用容量,不能用来推算剩余 VRAM。
14.7 内存释放流程
14.7.1 释放调用链
内存释放与分配对应,同样经过多层:
">
#mermaid-svg-qkzDTX0gx87JXBSX{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-qkzDTX0gx87JXBSX .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-qkzDTX0gx87JXBSX .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-qkzDTX0gx87JXBSX .error-icon{fill:#552222;}#mermaid-svg-qkzDTX0gx87JXBSX .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-qkzDTX0gx87JXBSX .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-qkzDTX0gx87JXBSX .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-qkzDTX0gx87JXBSX .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-qkzDTX0gx87JXBSX .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-qkzDTX0gx87JXBSX .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-qkzDTX0gx87JXBSX .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-qkzDTX0gx87JXBSX .marker{fill:#333333;stroke:#333333;}#mermaid-svg-qkzDTX0gx87JXBSX .marker.cross{stroke:#333333;}#mermaid-svg-qkzDTX0gx87JXBSX svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-qkzDTX0gx87JXBSX p{margin:0;}#mermaid-svg-qkzDTX0gx87JXBSX .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-qkzDTX0gx87JXBSX .cluster-label text{fill:#333;}#mermaid-svg-qkzDTX0gx87JXBSX .cluster-label span{color:#333;}#mermaid-svg-qkzDTX0gx87JXBSX .cluster-label span p{background-color:transparent;}#mermaid-svg-qkzDTX0gx87JXBSX .label text,#mermaid-svg-qkzDTX0gx87JXBSX span{fill:#333;color:#333;}#mermaid-svg-qkzDTX0gx87JXBSX .node rect,#mermaid-svg-qkzDTX0gx87JXBSX .node circle,#mermaid-svg-qkzDTX0gx87JXBSX .node ellipse,#mermaid-svg-qkzDTX0gx87JXBSX .node polygon,#mermaid-svg-qkzDTX0gx87JXBSX .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-qkzDTX0gx87JXBSX .rough-node .label text,#mermaid-svg-qkzDTX0gx87JXBSX .node .label text,#mermaid-svg-qkzDTX0gx87JXBSX .image-shape .label,#mermaid-svg-qkzDTX0gx87JXBSX .icon-shape .label{text-anchor:middle;}#mermaid-svg-qkzDTX0gx87JXBSX .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-qkzDTX0gx87JXBSX .rough-node .label,#mermaid-svg-qkzDTX0gx87JXBSX .node .label,#mermaid-svg-qkzDTX0gx87JXBSX .image-shape .label,#mermaid-svg-qkzDTX0gx87JXBSX .icon-shape .label{text-align:center;}#mermaid-svg-qkzDTX0gx87JXBSX .node.clickable{cursor:pointer;}#mermaid-svg-qkzDTX0gx87JXBSX .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-qkzDTX0gx87JXBSX .arrowheadPath{fill:#333333;}#mermaid-svg-qkzDTX0gx87JXBSX .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-qkzDTX0gx87JXBSX .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-qkzDTX0gx87JXBSX .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-qkzDTX0gx87JXBSX .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-qkzDTX0gx87JXBSX .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-qkzDTX0gx87JXBSX .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-qkzDTX0gx87JXBSX .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-qkzDTX0gx87JXBSX .cluster text{fill:#333;}#mermaid-svg-qkzDTX0gx87JXBSX .cluster span{color:#333;}#mermaid-svg-qkzDTX0gx87JXBSX div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-qkzDTX0gx87JXBSX .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-qkzDTX0gx87JXBSX rect.text{fill:none;stroke-width:0;}#mermaid-svg-qkzDTX0gx87JXBSX .icon-shape,#mermaid-svg-qkzDTX0gx87JXBSX .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-qkzDTX0gx87JXBSX .icon-shape p,#mermaid-svg-qkzDTX0gx87JXBSX .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-qkzDTX0gx87JXBSX .icon-shape .label rect,#mermaid-svg-qkzDTX0gx87JXBSX .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-qkzDTX0gx87JXBSX .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-qkzDTX0gx87JXBSX .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-qkzDTX0gx87JXBSX :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 应用程序
hsa_memory_free(ptr)
公共 API 层
• 查找 ptr 对应的 MemoryRegion
• 验证 ptr 有效性
MemoryRegion::Free(ptr)
• 从 allocation_map_ 查找信息
• 验证是否为本区域分配
• 调用 Agent 释放
Agent::FreeMemory(ptr, size)
• driver_->FreeMemory(ptr, size) ← 通过 Driver
KfdDriver::FreeMemory(ptr, size)
• hsaKmtFreeMemory(ptr)
KFD 内核驱动
• 解除 GPU 页表映射
• 释放物理内存
• 更新可用容量
14.7.2 释放时的清理工作
cpp
// MemoryRegion::Free实现
hsa_status_t MemoryRegion::Free(void* ptr) {
// ==================== 1. 查找分配信息 ====================
AllocationInfo* info = nullptr;
{
ScopedLock lock(&allocation_map_lock_);
auto it = allocation_map_.find(ptr);
if (it == allocation_map_.end()) {
return HSA_STATUS_ERROR_INVALID_ALLOCATION;
}
info = &it->second;
}
// ==================== 2. 调用Agent释放 ====================
hsa_status_t status = owner()->FreeMemory(ptr, info->size);
if (status != HSA_STATUS_SUCCESS) {
return status;
}
// ==================== 3. 清理记录 ====================
{
ScopedLock lock(&allocation_map_lock_);
allocation_map_.erase(ptr);
}
// ==================== 4. 更新统计 ====================
UpdateMemoryStatistics(-info->size);
return HSA_STATUS_SUCCESS;
}
关键点:
- 必须在allocation_map_中找到对应记录
- 释放成功后才从映射表删除
- 更新内存使用统计信息
14.8 内存碎片问题
14.8.1 碎片产生原因
初始状态: [ VRAM 16GB ]
分配A (4GB): [AAAA ]
分配B (2GB): [AAAA BB ]
分配C (6GB): [AAAA BB CCCCCC ]
分配D (1GB): [AAAA BB CCCCCC D ]
释放B: [AAAA -- CCCCCC D ]
↑ 2GB空闲,但不连续
分配E (3GB): 失败!虽然总空闲3GB (2+1),但无连续3GB空间
14.8.2 碎片缓解策略
策略1:分配器算法优化
最佳适配 (Best Fit):
• 查找最小的满足需求的空闲块
• 减少碎片产生
• 查找时间较长
首次适配 (First Fit):
• 使用第一个足够大的空闲块
• 速度快
• 可能产生更多碎片
伙伴系统 (Buddy System):
• 分配2的幂次大小
• 易于合并
• 可能浪费空间
策略2:预分配与池化
cpp
// 预分配常用大小的内存块
class MemoryPool {
private:
std::vector<void*> pool_4MB_;
std::vector<void*> pool_16MB_;
std::vector<void*> pool_64MB_;
public:
void* Allocate(size_t size) {
if (size <= 4MB && !pool_4MB_.empty()) {
void* ptr = pool_4MB_.back();
pool_4MB_.pop_back();
return ptr;
}
// 否则实际分配
return RealAllocate(size);
}
void Free(void* ptr, size_t size) {
if (size <= 4MB && pool_4MB_.size() < MAX_POOL_SIZE) {
// 归还到池中
pool_4MB_.push_back(ptr);
} else {
// 真正释放
RealFree(ptr);
}
}
};
策略3:碎片整理的现实约束
需要澄清一个常见误解:ROCr Runtime 用户态并不会自动"移动数据块 + 更新所有引用"做碎片整理。原因是:
- GPU 虚拟地址一旦分配即固定,会被写入内核参数(kernarg)、描述符、以及应用自己保存的指针中;
- 用户态无法安全地追踪并改写所有这些引用,强行搬迁会导致正在运行的 kernel 访问到错误地址。
因此真正可行的做法分两层:
- 应用/框架层:通过内存池(见策略2)、按固定尺寸分配、及时释放并复用,从源头减少碎片;必要时由应用主动"释放旧缓冲 → 重新分配 → 自己拷贝数据 → 更新自己的指针",这属于应用可控的显式重排,而非 Runtime 透明搬迁。
- 内核 amdgpu / TTM 层 :在物理层面,TTM 可以驱逐、迁移 BO 的物理位置,但保持 GPU 虚拟地址不变,因此对应用透明------这也不是用户态可见的"碎片整理"。
结论:不要期望 ROCr 在后台帮你压缩 VRAM 并重定位指针;缓解碎片主要靠分配策略 与内存池,而不是运行时透明的 compaction。
14.9 性能优化技巧
14.9.1 减少分配次数
cpp
// ❌ 差:频繁小分配
for (int i = 0; i < 1000; i++) {
void* ptr;
hsa_memory_allocate(region, 4096, &ptr);
// 使用...
hsa_memory_free(ptr);
}
// ✅ 好:批量分配
void* large_buffer;
hsa_memory_allocate(region, 1000 * 4096, &large_buffer);
for (int i = 0; i < 1000; i++) {
void* ptr = (char*)large_buffer + i * 4096;
// 使用...
}
hsa_memory_free(large_buffer);
14.9.2 选择合适的内存类型
cpp
// 根据访问模式选择内存类型
// 场景1: GPU密集计算,CPU很少访问
// → 使用VRAM (Coarse-grained)
hsa_memory_allocate(gpu_local_region, size, &ptr);
// 场景2: CPU-GPU频繁交互
// → 使用System Memory (Fine-grained)
hsa_memory_allocate(system_fine_region, size, &ptr);
// 场景3: 大数据集,低访问频率
// → 使用System Memory (Coarse-grained) + Explicit Copy
hsa_memory_allocate(system_coarse_region, size, &ptr);
14.9.3 利用内存池API
cpp
// AMD扩展:使用内存池
hsa_amd_memory_pool_t gpu_pool;
hsa_amd_agent_iterate_memory_pools(
gpu_agent,
[](hsa_amd_memory_pool_t pool, void* data) {
// 查找VRAM池
hsa_amd_segment_t segment;
hsa_amd_memory_pool_get_info(
pool, HSA_AMD_MEMORY_POOL_INFO_SEGMENT, &segment);
if (segment == HSA_AMD_SEGMENT_GLOBAL) {
*(hsa_amd_memory_pool_t*)data = pool;
return HSA_STATUS_INFO_BREAK;
}
return HSA_STATUS_SUCCESS;
},
&gpu_pool);
// 从池中分配
void* ptr;
hsa_amd_memory_pool_allocate(gpu_pool, size, 0, &ptr);
14.10 调试内存问题
14.10.1 常见内存错误
| 错误类型 | 症状 | 排查方法 |
|---|---|---|
| 内存泄漏 | 可用内存持续下降 | 使用valgrind、rocprofiler追踪分配 |
| 重复释放 | Segment fault | 启用HSA_DEBUG,检查释放路径 |
| 越界访问 | 数据损坏、崩溃 | 使用AddressSanitizer |
| 对齐错误 | 性能下降、崩溃 | 检查指针对齐,使用aligned_alloc |
| 类型错误 | 访问错误的内存 | 确认region类型和访问agent匹配 |
14.10.2 环境变量诊断
bash
# 启用内存分配追踪
export HSA_ENABLE_DEBUG=1
export HSA_MEMORY_DEBUG=1
# 输出详细日志
export HSA_DEBUG_LEVEL=7
# 每次分配都记录
export HSA_TOOLS_LIB=libhsa-runtime-tools64.so
# 运行程序
./my_app
14.10.3 Driver层调试
cpp
// 在Driver层添加日志
hsa_status_t KfdDriver::AllocMemory(...) {
#ifdef HSA_DEBUG
fprintf(stderr, "[Driver] AllocMemory: node=%u, size=%zu, flags=%x\n",
node_id, size, flags.Value);
#endif
HSAKMT_STATUS status = hsaKmtAllocMemory(...);
#ifdef HSA_DEBUG
fprintf(stderr, "[Driver] AllocMemory: result=%d, addr=%p\n",
status, *mem);
#endif
return MapKmtError(status);
}
🎯 本章总结
核心要点回顾
-
分配流程分层
- API层 → MemoryRegion → Agent → Driver → Thunk → KFD
- 每层有明确职责,Driver是硬件操作的唯一通道
-
Driver的关键作用
- 参数转换(HSA ↔ KFD)
- 接口封装(C++ ↔ C)
- 错误处理和状态管理
- GPU地址空间映射
-
内存类型差异
- System Memory:主机内存,需要映射到GPU
- VRAM:GPU显存,高带宽但容量有限
-
对齐与粒度
- 不同内存类型有不同对齐要求
- 大页支持可提升大内存性能
-
VRAM管理
- 容量有限,需要精心管理
- 碎片问题需要预防和缓解
-
性能优化
- 批量分配减少开销
- 选择合适的内存类型
- 使用内存池API
🔗 导航
- 上一章 : 13-Memory-Region内存区域
- 下一章: 15-统一内存与一致性审核中...
- 返回目录 : 00-HSA-Runtime学习文档目录