Memory 模块架构
1. 模块概述
- 功能介绍:Memory 模块负责管理设备内存的分配、操作和释放。提供了一套内存管理API,使开发者能够高效便捷地管理应用程序中的内存使用。
- 设计目标:
- 提供统一的内存管理接口
- 支持多种内存类型
- 支持高效的内存拷贝
- 支持进程间内存共享
2. 使用场景与对外接口
2.1 基本使用场景
-
场景一:分配Device内存
void *devPtr; aclrtMalloc(&devPtr, size, ACL_MEM_MALLOC_HUGE_FIRST); -
场景二:分配Host内存
void *hostPtr; aclrtMallocHost(&hostPtr, size); -
场景三:同步内存拷贝
aclrtMemcpy(dstPtr, dstSize, srcPtr, srcSize, ACL_MEMCPY_HOST_TO_DEVICE); -
场景四:异步内存拷贝
aclrtStream stream; aclrtCreateStream(&stream); // 异步拷贝,不阻塞主机 aclrtMemcpyAsync(dstPtr, dstSize, srcPtr, srcSize, ACL_MEMCPY_HOST_TO_DEVICE, stream); -
场景五:IPC 内存共享
// 进程 A:导出共享句柄 void *ptr; aclrtMalloc(&ptr, size, ACL_MEM_MALLOC_HUGE_FIRST); // 提前申请设备内存 char key[65]; aclrtIpcMemGetExportKey(ptr, size, key, 65, ACL_RT_IPC_MEM_EXPORT_FLAG_DEFAULT); // 进程 B:导入共享内存 void *sharedPtr; aclrtIpcMemImportByKey(&sharedPtr, key, ACL_RT_IPC_MEM_IMPORT_FLAG_DEFAULT); // 使用完成后关闭 IPC 内存 aclrtIpcMemClose(key); aclrtFree(ptr); // 进程 A 释放原内存 -
场景六:物理内存管理
aclrtDrvMemHandle handle; aclrtPhysicalMemProp prop = {...}; aclrtMallocPhysical(&handle, size, &prop, 0); // 分配物理内存 void *virPtr; aclrtReserveMemAddress(&virPtr, size, 0, nullptr, 0); // 预留虚拟地址空间 aclrtMapMem(virPtr, size, 0, handle, 0); // 映射物理内存到虚拟地址 // 使用完成后解除映射并释放内存 aclrtUnmapMem(virPtr); // 解除映射 aclrtReleaseMemAddress(virPtr); // 释放虚拟地址空间 aclrtFreePhysical(handle); // 释放物理内存
2.2 对外接口
| 接口 | 说明 |
|---|---|
| 内存分配/释放 | |
aclrtMalloc/aclrtMallocWithCfg |
分配设备内存 |
aclrtMallocHost/aclrtMallocHostWithCfg |
分配主机内存 |
aclrtFree |
释放设备内存 |
aclrtFreeHost |
释放主机内存 |
| 内存拷贝 | |
aclrtMemcpy/aclrtMemcpyAsync |
内存同步/异步拷贝 |
aclrtMemcpy2d/aclrtMemcpy2dAsync |
2D 内存同步/异步拷贝 |
aclrtMemcpyBatch/aclrtMemcpyBatchAsync |
批量内存同步/异步拷贝 |
| 内存设置 | |
aclrtMemset |
内存设置 |
aclrtMemsetAsync |
异步内存设置 |
| 物理内存管理 | |
aclrtMallocPhysical |
分配物理内存 |
aclrtReserveMemAddress |
预留虚拟地址空间 |
aclrtMapMem |
映射物理内存到虚拟地址 |
aclrtUnmapMem |
解除内存映射 |
aclrtReleaseMemAddress |
释放虚拟地址空间 |
aclrtFreePhysical |
释放物理内存 |
| 内存注册 | |
aclrtHostRegister |
注册主机内存 |
aclrtHostUnregister |
取消主机内存注册 |
2.3 内存拷贝类型
| 拷贝类型 | 说明 |
|---|---|
ACL_MEMCPY_HOST_TO_HOST |
主机到主机 |
ACL_MEMCPY_HOST_TO_DEVICE |
主机到设备 |
ACL_MEMCPY_DEVICE_TO_HOST |
设备到主机 |
ACL_MEMCPY_DEVICE_TO_DEVICE |
设备到设备 |
ACL_MEMCPY_DEFAULT |
默认拷贝类型(系统自动推断方向) |
ACL_MEMCPY_HOST_TO_BUF_TO_DEVICE |
主机到设备(Runtime中会缓存,应用调用完接口就可以释放Host内存) |
ACL_MEMCPY_INNER_DEVICE_TO_DEVICE |
同设备内部拷贝 |
ACL_MEMCPY_INTER_DEVICE_TO_DEVICE |
跨设备拷贝 |
3. 架构总览
架构分层图
graph TB
subgraph API["接口层"]
内存申请释放["内存申请释放"]
内存同步操作["内存同步操作"]
内存异步操作["内存异步操作"]
IPC共享内存["IPC共享内存"]
虚拟内存管理["虚拟内存管理"]
end
subgraph MemoryCore["Runtime 框架层"]
内存管理["内存管理"]
组装任务["组装任务"]
end
subgraph AdapterLayer["适配层"]
HAL接口["HAL 接口"]
end
subgraph DriverLayer["驱动层"]
Driver["Driver"]
end
内存申请释放 --> 内存管理
内存同步操作 --> 内存管理
IPC共享内存 --> 内存管理
虚拟内存管理 --> 内存管理
内存异步操作 --> 组装任务
内存管理 --> HAL接口
组装任务 --> HAL接口
HAL接口 --> Driver
4. 详细设计
4.1 核心流程
内存申请流程
Device申请内存的接口如下:
aclError aclrtMalloc(void **devPtr, size_t size, aclrtMemMallocPolicy policy);
devPtr:指向分配内存地址的二级指针,函数成功后*devPtr为分配的设备内存地址;size:申请内存大小,单位为字节;policy:内存分配策略,根据应用实际场景与诉求,开发者可以指定申请策略,选择普通的小页(4KB)更加节省资源,在处理海量数据时使用大页(2MB)性能更好。支持的策略说明如下:
| 策略 | 说明 |
|---|---|
ACL_MEM_MALLOC_HUGE_FIRST |
优先使用大页内存,大页内存不足时使用普通页内存 |
ACL_MEM_MALLOC_HUGE_ONLY |
仅使用大页内存,大页内存不足时返回错误 |
ACL_MEM_MALLOC_NORMAL_ONLY |
仅使用普通页内存 |
ACL_MEM_MALLOC_HUGE_FIRST_P2P |
优先使用大页内存(P2P场景) |
ACL_MEM_MALLOC_HUGE_ONLY_P2P |
仅使用大页内存(P2P场景) |
ACL_MEM_MALLOC_NORMAL_ONLY_P2P |
仅使用普通页内存(P2P场景) |
ACL_MEM_MALLOC_HUGE1G_ONLY |
仅使用1G大页内存 |
ACL_MEM_MALLOC_HUGE1G_ONLY_P2P |
仅使用1G大页内存(P2P场景) |
ACL_MEM_TYPE_LOW_BAND_WIDTH |
低带宽内存类型 |
ACL_MEM_TYPE_HIGH_BAND_WIDTH |
高带宽内存类型 |
ACL_MEM_ACCESS_USER_SPACE_READONLY |
用户空间只读访问权限 |
详细申请流程如下所示:
sequenceDiagram
participant App as 应用程序
participant API as API
participant Manger as 内存管理
participant Adapter as HAL
participant HW as 驱动
App->>API: aclrtMalloc
API->>Manger: DevMalloc
Manger->>Adapter: DevMemAlloc
Adapter->>HW: halMemAlloc
HW-->>App: devAddr
内存拷贝流程
常用的内存拷贝分为同步拷贝和异步拷贝:
同步拷贝(aclrtMemcpy):
- 调用后阻塞主机线程,直到拷贝完成才返回
- 适用场景:需要立即使用拷贝结果、数据量较小、简单的数据传输场景
- 优点:使用简单,不需要考虑流同步问题
- 缺点:阻塞主机,影响并发性能
异步拷贝(aclrtMemcpyAsync):
- 调用后立即返回,不阻塞主机线程,拷贝任务在指定Stream上异步执行
- 适用场景:数据量大、需要与其他计算任务并行、多流并发场景
- 优点:提高主机与设备并行度,充分利用硬件性能
- 缺点:需要通过Event或Stream同步确保拷贝完成后再使用数据
以下是异步拷贝的主要流程:
sequenceDiagram
participant App as 应用程序
participant API as API
participant Manger as 内存管理
participant Adapter as HAL
participant HW as 驱动
participant NPU as NPU
App->>API: aclrtMemcpyAsync
API->>Manger: 组装拷贝任务
Manger->>Adapter: 提交拷贝任务
Adapter->>HW: 下发拷贝任务
HW-->>App: 返回下发结果
HW->>NPU: 等待执行
批量内存拷贝流程
批量内存拷贝允许一次性提交多个内存拷贝任务,减少任务提交开销,提高整体拷贝效率。支持同步批量拷贝(aclrtMemcpyBatch)和异步批量拷贝(aclrtMemcpyBatchAsync),适用于需要同时传输多个数据块的场景。
接口参数:
aclError aclrtMemcpyBatch(void **dsts, size_t *destMaxs, void **srcs,
size_t *sizes, size_t numBatches,
aclrtMemcpyBatchAttr *attrs, size_t *failIdx);
aclError aclrtMemcpyBatchAsync(void **dsts, size_t *destMaxs, void **srcs,
size_t *sizes, size_t numBatches,
aclrtMemcpyBatchAttr *attrs, size_t *failIdx,
aclrtStream stream);
参数说明:
| 参数 | 类型 | 说明 |
|---|---|---|
dsts |
IN void** |
目标地址数组 |
destMaxs |
IN size_t* |
目标地址最大容量数组 |
srcs |
IN void** |
源地址数组 |
sizes |
IN size_t* |
拷贝大小数组 |
numBatches |
IN size_t |
批量拷贝任务数量 |
attrs |
IN aclrtMemcpyBatchAttr* |
拷贝属性数组(可指定每个任务的拷贝方向) |
failIdx |
OUT size_t* |
失败任务索引(同步拷贝时返回) |
stream |
IN aclrtStream |
异步拷贝使用的流 |
物理内存管理流程
物理内存管理允许用户直接控制物理内存的分配和映射,提供更精细的内存管理能力。主要用于需要跨进程共享内存、大内存池管理或需要特定内存属性的进阶场景。通过物理内存管理接口,用户可以:
- 独立分配物理内存并获取 handle(
aclrtMallocPhysical) - 预留虚拟地址空间(
aclrtReserveMemAddress) - 将物理内存映射到虚拟地址空间(
aclrtMapMem) - 使用完成后解除映射并释放资源(
aclrtUnmapMem、aclrtReleaseMemAddress、aclrtFreePhysical)
flowchart TD
A[aclrtMallocPhysical] --> B[Driver 分配物理内存]
B --> C[返回 handle]
D[aclrtReserveMemAddress] --> E[预留虚拟地址空间]
E --> F[返回 virPtr]
G[aclrtMapMem] --> H[关联 handle 和 virPtr]
H --> I[建立映射关系]
I --> J[可访问物理内存]
K[aclrtUnmapMem] --> L[解除映射]
L --> M[释放虚拟地址]
N[aclrtReleaseMemAddress] --> O[释放虚拟地址空间]
P[aclrtFreePhysical] --> Q[释放物理内存]
IPC 内存共享
IPC(Inter-Process Communication)内存共享允许不同进程之间共享同一块设备内存,实现高效的跨进程数据传递。通过 IPC 机制,进程 A 可以将已分配的设备内存导出为共享 key,进程 B 通过该 key 导入并访问同一块物理内存,避免了数据拷贝开销。
| 核心接口 | 说明 |
|---|---|
aclrtIpcMemGetExportKey |
导出设备内存为 IPC key,供其他进程导入 |
aclrtIpcMemImportByKey |
通过 key 导入共享内存到当前进程 |
aclrtIpcMemClose |
关闭 IPC 共享内存(导出方调用) |
aclrtIpcMemSetImportPid |
设置允许导入的进程 PID 白名单 |
sequenceDiagram
participant ProcA as 进程 A(导出方)
participant API_A as API(进程 A)
participant Driver as Driver/内核
participant API_B as API(进程 B)
participant ProcB as 进程 B(导入方)
ProcA->>API_A: aclrtMalloc(分配设备内存)
API_A-->>ProcA: devPtr
ProcA->>API_A: aclrtIpcMemGetExportKey(devPtr, size)
API_A->>Driver: 创建 IPC 共享句柄
Driver-->>API_A: 返回 key
API_A-->>ProcA: key(64字节字符串)
Note over ProcA,ProcB: 进程 A 通过 IPC 机制传递 key 给进程 B
ProcB->>API_B: aclrtIpcMemImportByKey(key)
API_B->>Driver: 导入共享内存
Driver->>Driver: 映射到进程 B 地址空间
Driver-->>API_B: sharedPtr
API_B-->>ProcB: sharedPtr(可访问同一块物理内存)
Note over ProcA,ProcB: 进程 A 和进程 B 共享同一块物理内存
ProcB->>ProcB: 使用 sharedPtr 访问数据
ProcA->>ProcA: 使用 devPtr 访问数据
ProcB->>API_B: aclrtFree(sharedPtr)
ProcA->>API_A: aclrtIpcMemClose(key)
ProcA->>API_A: aclrtFree(devPtr)
4.2 关键文件索引
| 功能模块 | 文件路径 | 核心内容 |
|---|---|---|
| ACL 内存接口 | include/external/acl/acl_rt.h |
aclrtMalloc、aclrtMemcpy、aclrtIpcMem 等对外接口 |
| Runtime 接口层实现 | src/runtime/api/api_c_memory.cc |
rtMem* 内部接口实现 |
| 内存任务 | src/runtime/core/src/task/task_info/memory/memory_task.cc |
MemcpyAsyncTask、MemcpyBatchTask 任务实现 |
| 内存任务头文件 | src/runtime/core/src/task/inc/memory_task.h |
内存任务接口定义 |