CANN / ge 内存约束文档
GE 内存约束文档
【免费下载链接】geGE(Graph Engine)是面向昇腾的图编译器和执行器,提供了计算图优化、多流并行、内存复用和模型下沉等技术手段,加速模型执行效率,减少模型内存占用。 GE 提供对 PyTorch、TensorFlow 前端的友好接入能力,并同时支持 onnx、pb 等主流模型格式的解析与编译。项目地址: https://gitcode.com/cann/ge
静态内存复用
代码位置:compiler/graph/build/memory/
约束1:图编译模块内存复用处理阶段禁止改图
精确边界:
- 禁止改图的范围:
BlockMemAssigner::AssignMemoryWithReuse实现及其触发的所有函数 - 多线程入口:
HybridMemAssigner::Assign启动多线程,并发调用AssignMemoryWithReuse - 明确禁止的:对ComputeGraph上的Node增加、修改、删除属性
- 安全的操作:读取属性/OpDesc是安全的(系统在遍历时大量读取OpDesc来判断内存分配策略)
约束2:动态多batch场景影响分析
- 代码实体:
DynamicBatchMemAssigner(dynamic_batch_mem_assigner.h) - 含义:系统通过
batch_label_(由用户或GE上层框架设置)标识不同batch,支持不同batch间的内存复用策略 - 与静态内存复用的矛盾点:
- 连续输入的内存在不同batch中会被合并成一个大块进行对齐
- batch内外没有复用,batch间有对齐策略,导致内存使用效率可能降低
- 最大拆分大小限制:
kMaxSplitSizeForDynamicBatch = 400MB(dynamic_batch_mem_assigner.h)
约束3:静态图内存新增特性需要考虑的场景
| 场景 | 代码标记/依据 | 对复用的影响 |
|---|---|---|
| 连续内存 | continuous_block_(block_mem_assigner.h),ContinuousMemMng(continuous_mem.cc) | 支持连续输入节点的内存合并;不同batch的连续内存可以合并复用 |
| atomic集中清零 | atomic_addr_clean_id_(block_mem_assigner.h) | 需要atomic清零的内存块不能被其他节点复用;如果节点没有相关属性则跳过清零 |
| 零拷贝 | is_zero_copy_(block_mem_assigner.h),IsNodeAndPeerNodeTaskSupportZeroCopy(block_mem_assigner.cc) | 零拷贝块可跨节点复用(IsRealSizeReuseBlock);零拷贝内存不能合并(多个用户输入地址可能不连续) |
| 不可变地址输出 | is_fixed_addr_prior_(block_mem_assigner.h) | constant/const/variable/fileconstant/constplaceholder类型的算子output地址在编译期固定,固定地址优先的内存块可被复用但地址不可变 |
| 不支持地址刷新的算子 | HCOM/rtsStreamSwitchByIndex等 | 这些算子的输入输出地址必须稳定,不能使用零拷贝 |
| P2P内存类型 | RT_MEMORY_P2P_DDR(block_mem_assigner.cc) | P2P内存不能与其他内存类型合并清零(graph_mem_assigner.cc) |
约束4:HCOM算子的特殊性
- "连续"的含义:逻辑连续而非物理连续。多个HCOM算子的输出在逻辑上形成连续内存区域,通过
ContinuousMemMng管理器处理分配和复用。 —continuous_mem.cc - featureBaseRefreshable配置:
- 获取方式:
ge::GetContext().GetOption(ge::OPTION_FEATURE_BASE_REFRESHABLE, refreshable)—block_mem_assigner.cc - 成员变量:
is_feature_map_refreshable_(block_mem_assigner.h) - 默认值:
false,配置值为"1"时设为true - 作用:控制特征图是否可刷新,影响
IsNoNeedAssignMemory的判断
- 获取方式:
约束5:其他约束
PreAssign/SetOpMemOffset非线程安全:只能由单线程调用,其他并发操作需要注意。 —
block_mem_assigner.h对齐策略差异:零拷贝内存使用32字节对齐,其他使用512字节对齐。 —
graph_mem_assigner.h子图NETOUTPUT特殊处理:子图中的NETOUTPUT节点不能进行零拷贝。 —
block_mem_assigner.cc多batch形状数据节点约束:多batch形状数据节点不支持零拷贝。 —
block_mem_assigner.cc悬挂内存块管理:悬挂的内存块(suspended block)会在下一个节点分配时被释放,生命周期通过
life_time_begin_和life_time_end_管理,一旦设置不能修改。 —block_mem_assigner.h复用策略可配置:支持通过
use_range_、ascending_sort_、reuse_first_release_、memory_priority_mode_等参数动态配置。 —block_mem_assigner.h
动态内存复用
代码位置:
- v2层:
runtime/v2/kernel/memory/allocator/(ScalableAllocator、MemoryPool) - v1层:
runtime/v1/graph/manager/active_memory_allocator.h(ActiveMemoryAllocator、ExpandableActiveMemoryAllocator、PhysicalMemoryAllocator) - 桥接层:
runtime/v2/kernel/memory/device/device_allocator.h(DeviceAllocator)
约束1:ScalableAllocator不支持多线程并发
- 代码位置:
runtime/v2/kernel/memory/allocator/scalable_allocator.h - 无锁设计依据:类内部无
std::mutex或std::recursive_mutex,仅有static std::atomic_size_t global_allocator_id_用于生成唯一ID(scalable_allocator.h) - 安全性保证方式:由
aclmdlExecute的调用约束保证单线程调用(详见docs/graph_engine_api/aclmdlExecute.md),底层allocator通过recursive_mutex保证线程安全 - 底层有锁保护:v1层的PhysicalMemoryAllocator使用
std::recursive_mutex(active_memory_allocator.h),ExpandableActiveMemoryAllocatorImp同样使用std::recursive_mutex(active_memory_allocator.h)
约束2:ActiveMemoryAllocator/ExpandableActiveMemoryAllocator/PhysicalMemoryAllocator支持多线程
- 线程安全机制:使用
std::recursive_mutex保护共享资源 - 新增代码要求:必须在访问共享资源时加锁保护,遵循现有的锁使用模式
内存管理
代码位置:runtime/v2/kernel/memory/(不含allocator子目录)
约束1:device id正确性
- 代码位置:
memory_kernel.cc使用aclrtGetDevice获取device_id - 要求:调用rts接口时,device id必须显式传入正确值,避免使用缺省参数(默认为0),需要验证多device场景用例
约束2:内存释放时序
- 顺序:先流同步 → 再释放内存 → 最后销毁device
- 代码关联:
caching_mem_allocator.cc,AllocateWithTryRecycle方法确保同步后再释放
约束3:虚拟内存兼容性设计
- rtReserveMemAddress用途:虚拟地址预留,用于动态shape预分配地址空间
- 实际调用位置:
runtime/v1/graph/manager/active_memory_allocator.cc - fallback路径:当
rtReserveMemAddress失败时,标记不支持虚拟地址预留,回退到物理地址分配模式。 —runtime/v1/graph/manager/active_memory_allocator.cc("Maybe not support rtReserveMemAddress.") - 要求:要确保业务流程正常,无ERROR日志
约束4:caching_mem_allocator进程退出时序
- 代码位置:
caching_mem_allocator.h—static std::vector<CachingMemAllocator *> all_caching_mem_allocators_ - 机制:全局变量保存所有allocator实例指针,GE finalize时主动调用所有allocator的finalize方法
- 原因:allocator析构时如果还有内存未释放,会调用rts接口释放,但此时rts so可能已卸载,导致core dump
- finalize入口:
rts_caching_mem_allocator.cc遍历all_caching_mem_allocators_逐一finalize
约束5:单例内存池在模型卸载或者session析构时也要析构
举例SessionMemAllocator<ExpandableActiveMemoryAllocator>是单例,内部存储基于session id的对象。当用户创建新的session,或者调用aclmdl开头的接口进行离线推理时会产生新的session id,也就会产生新的对象,因此需要在下面两个地方调用SessionMemAllocator<ExpandableActiveMemoryAllocator>::Instance().RemoveAllocator释放对象内存,否则用户在创建多个session或者多次执行调用aclmdl开头的接口进行离线推理的场景下,无用的对象会多占用host内存或者device内存资源,造成“内存泄漏”现象。
- GeExecutor::UnloadModel
aclmdl离线推理场景卸载模型 - InnerSession::Finalize InnerSession析构
CachingMemAllocator完整关系图
┌──────────────────────────────────────────────────────────────┐ │ CachingMemAllocator │ │ runtime/v2/kernel/memory/caching_mem_allocator.h │ │ - all_caching_mem_allocators_ (全局allocator注册表) │ │ - rts_mem_allocator_ (RtsFirstLevelPool) │ │ - memory_pool_ → ScalableAllocator │ │ - mutex_ (static, 保护allocator创建/销毁) │ ├──────────────────────────────────────────────────────────────┤ │ │ │ ┌── RtsCachingMemAllocator │ │ │ runtime/v2/kernel/memory/rts_caching_mem_allocator.h │ │ │ - device_id_to_allocators_ (device → allocator映射) │ │ │ - 负责finalize流程,遍历all_caching_mem_allocators_ │ │ │ │ │ └── ScalableAllocator (通过memory_pool_指针) │ │ runtime/v2/kernel/memory/allocator/scalable_allocator │ │ - 无锁设计,单线程调用 │ │ - 通过 DeviceAllocator 桥接到 v1 allocator │ │ │ │ └── DeviceAllocator │ │ runtime/v2/kernel/memory/device/device_allocator │ │ - active_memory_allocator_ (Expandable...Imp, v1) │ │ │ │ └── PhysicalMemoryAllocator (v1) │ │ runtime/v1/graph/manager/active_mem...h │ │ - recursive_mutex 保护 │ │ - 最终调用 rtMalloc / rtFree │ └──────────────────────────────────────────────────────────────┘编译期内存排布冲突
代码位置:compiler/graph/optimize/mem_layout_conflict_optimize/
维测日志约束
- 发现冲突:
[MemConflict][Conflict] type: [%s, %s], anchor: [%s, %s], will insert %s %s - 插入Identity:
[MemConflict][INSERT][NODE] - 日志使用
[MemConflict]关键字,如果每张图只会打印一次,使用LOGI
跨模块约束汇总
以下约束跨越多个模块,修改时需同步考虑:
地址刷新能力(模块一 + 模块二):mem_layout_conflict_optimize中的冲突检测判定"不支持地址刷新"的算子列表,必须与静态内存复用中的处理逻辑保持一致。
v1/v2 allocator边界(模块三 + 模块四):v2的ScalableAllocator通过DeviceAllocator桥接到v1的PhysicalMemoryAllocator。修改任何一层的接口都需要考虑对另一层的影响。
caching_mem_allocator生命周期(模块三 + 模块四):CachingMemAllocator的finalize流程依赖
all_caching_mem_allocators_全局注册表,新增allocator类型必须正确注册到此表中。
【免费下载链接】geGE(Graph Engine)是面向昇腾的图编译器和执行器,提供了计算图优化、多流并行、内存复用和模型下沉等技术手段,加速模型执行效率,减少模型内存占用。 GE 提供对 PyTorch、TensorFlow 前端的友好接入能力,并同时支持 onnx、pb 等主流模型格式的解析与编译。项目地址: https://gitcode.com/cann/ge
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
