CANN/GE模型缓存特性

CANN/GE模型缓存特性 GE 模型缓存Model Cache特性【免费下载链接】geGEGraph Engine是面向昇腾的图编译器和执行器提供了计算图优化、多流并行、内存复用和模型下沉等技术手段加速模型执行效率减少模型内存占用。 GE 提供对 PyTorch、TensorFlow 前端的友好接入能力并同时支持 onnx、pb 等主流模型格式的解析与编译。项目地址: https://gitcode.com/cann/ge1. 特性介绍模型缓存Model Cache是 GE 提供的编译结果持久化机制它将图编译产出的 OMOffline Model文件及变量描述信息保存到磁盘。当相同图结构再次编译时系统可以直接加载缓存产物跳过完整的编译流程包括图优化、内存规划、任务生成等阶段显著减少编译耗时。GE 的模型缓存体系覆盖三个层次层次核心类源文件适用场景图编译缓存ModelCachecompiler/graph/build/model_cache.hGeSession 编译整图时避免重复编译JIT 编译缓存CompiledModelCacheapi/session/jit_execution/cache/compiled_model_cache.hJIT 执行模式下缓存断图结果和子图编译产物算子模型缓存OpModelCacheapi/acl/acl_op_executor/single_op/op_model_cache.hACL 单算子执行时缓存已加载的算子模型2. 背景介绍2.1 解决的问题深度学习模型的图编译是一个计算密集型过程包括图优化 Pass 链常量折叠、死代码消除、算子融合、内存布局优化等数十个优化阶段引擎分区与流分配将算子分配到不同硬件引擎AI Core、Vector Core、Host CPU规划执行流内存规划为所有张量分配设备内存地址包括复杂的内存复用策略任务生成将编译后的图转换为硬件可执行的任务序列对于相同或相似的图结构重复执行上述全流程会造成不必要的等待。尤其在以下场景中编译耗时的优化需求尤为迫切服务化部署服务重启后需要重新编译加载模型冷启动时间直接影响服务可用性多 Session 共享不同 Session 加载同一模型时每个 Session 都会触发一次完整编译变量格式变更当变量的格式Format或 Shape 发生变化时图需要重新编译但编译结果可以复用于后续相同状态的编译JIT 执行模式动态图场景下GuardedExecutionPointGEP的编译结果跨进程复用2.2 设计目标透明使用用户通过 Session 选项即可启用无需修改模型代码变量感知缓存校验需要考虑变量的格式、Shape 等状态确保缓存在变量变化时正确失效并发安全支持多进程/多线程并发访问同一缓存目录通过文件锁保证互斥可调试提供 debug 模式启用缓存查找但不加载缓存结果用于验证缓存是否正确生成3. 开启方式模型缓存通过两个 Session 选项联合控制选项含义示例值ge.graph_compiler_cache_dir缓存文件存储目录必须为已存在的目录./cache_dirge.graph_key图的唯一标识用于区分不同图的缓存my_model_v1选项定义位于inc/graph_metadef/external/ge_common/ge_api_types.hconst char_t *const OPTION_GRAPH_COMPILER_CACHE_DIR ge.graph_compiler_cache_dir; const char_t *const OPTION_GRAPH_KEY ge.graph_key;启用条件两个选项必须同时设置且均不为空否则缓存功能不启用。3.1 通过 GeSession 启用std::mapAscendString, AscendString options; options.emplace(ge::OPTION_GRAPH_COMPILER_CACHE_DIR, ./build_cache_dir); options.emplace(ge::OPTION_GRAPH_KEY, test_graph_001); options.emplace(ge::OPTION_GRAPH_RUN_MODE, 1); GeSession session(options); auto graph BuildMyGraph(); session.AddGraph(graph_id, graph); session.CompileGraph(graph_id); // 首次编译会写入缓存 // ... 后续相同 graph_key 的编译会命中缓存3.2 缓存配置文件cache.conf用户可在缓存目录下放置可选的cache.conf配置文件JSON 格式控制缓存行为{ cache_manual_check: false, cache_debug_mode: false }配置项类型默认值说明cache_manual_checkboolfalse手动校验模式启用后 UDF 子图需要手动确认缓存cache_debug_modeboolfalse调试模式启用后仅查找缓存但不加载即每次仍执行完整编译用于验证缓存是否生成成功配置读取逻辑位于ModelCache::ReadCacheConfig()compiler/graph/build/model_cache.cc如果文件不存在则使用默认值。3.3 graph_key 命名规则graph_key必须匹配正则表达式^[A-Za-z0-9_\-]{1,128}$即仅允许字母、数字、下划线和连字符长度 1-128。这是因为graph_key会直接用作文件名前缀用于生成.om、.idx、.rdcpkt等缓存文件。校验逻辑在ModelCache::IsMatchFileName()中。4. 使用场景4.1 场景一服务冷启动加速首次部署 Session(ge.graph_compiler_cache_dir/data/cache, ge.graph_keyresnet50_v2) → CompileGraph() → 完整编译 → 缓存写入 /data/cache/resnet50_v2_20260415_102030.om 服务重启 Session(ge.graph_compiler_cache_dir/data/cache, ge.graph_keyresnet50_v2) → CompileGraph() → 检测到缓存 → 变量描述匹配 → 直接加载 .om → 跳过编译4.2 场景二多 Session 图共享多个 Session 可以通过设置相同的graph_compiler_cache_dir和graph_key共享编译缓存。第一个 Session 编译后写入缓存后续 Session 直接加载。4.3 场景三结合外置权重模型缓存常与外置权重External Weight特性配合使用。开启外置权重后权重数据不会嵌入 OM 文件而是存储在独立的权重文件中位于cache_dir/weight/目录进一步加速编译产物的序列化和加载。用户可通过ge.externalWeight和ge.externalWeightDir选项控制外置权重行为。4.4 场景四JIT 执行中的 GuardedExecutionPoint 缓存在 JITJust-In-Time执行模式下CompiledModelCache管理整张 UserGraph 的编译缓存包括ExecutionOrderEO断图策略和执行顺序ExecutionPointEP执行切分点GuardedExecutionPointGEP带守卫条件的执行点每个 GEP 有独立的gep_graph_key缓存目录结构{cache_dir}/ ├── jit/ # CompiledModelCache 根目录 │ ├── slicing_result.json # 断图结果 │ ├── {slice_graph_id}/ # 每个 slice graph 的子目录 │ │ ├── gep_list.json # GEP 列表 │ │ ├── slice_graph.pb # 子图序列化 │ │ └── rem_graph.pb # 剩余图序列化4.5 场景五ACL 单算子缓存在 ACL 单算子执行路径中OpModelCache以opModelId, OpModel键值对的形式在内存中缓存已加载的算子模型。这是一个纯内存缓存不涉及磁盘持久化主要目的是避免同一算子模型在同一进程内重复加载和初始化。5. 实现方式5.1 整体架构5.2 核心流程缓存的核心入口在GraphManager::BuildModel()compiler/graph/manager/graph_manager.cc5.3 初始化阶段ModelCache::Init初始化阶段完成缓存环境的准备工作主要逻辑如下读取选项从线程局部上下文GetThreadLocalContext()获取ge.graph_compiler_cache_dir和ge.graph_key校验选项如果任一为空设置cache_enable_ false直接返回校验目录检查缓存目录是否存在不存在则报错PARAM_INVALID读取配置读取cache_dir/cache.conf如存在解析cache_manual_check和cache_debug_mode校验 graph_key确保graph_key可以作为合法文件名文件锁在缓存目录下创建{graph_key}.lock文件并通过flock(LOCK_EX)加排他锁防止多进程并发冲突。锁在ModelCache析构时释放初始化缓存文件信息查找已有的缓存文件5.4 缓存查找与加载TryLoadModelFromCache缓存加载分为两步缓存文件定位和变量描述匹配。5.4.1 缓存文件定位InitCacheFileInfo()按以下优先级查找缓存文件直接匹配如果缓存目录下存在{graph_key}.om直接使用兼容无索引文件的旧格式索引查找读取{graph_key}.idx索引文件JSON 格式按graph_key匹配对应的缓存条目索引文件.idx格式{ cache_file_list: [ { graph_key: my_model_v1, cache_file_name: my_model_v1_20260415_102030.om, var_desc_file_name: my_model_v1_20260415_102030.rdcpkt } ] }一个graph_key可以对应多条缓存记录不同时间戳系统会逐一尝试匹配。5.4.2 变量描述匹配缓存命中的关键判断是变量描述VarDesc匹配。这是因为图中的 Variable 算子的格式、Shape 等属性可能在编译过程中发生变化如格式推导、广播处理等。如果变量的当前状态与缓存不一致则缓存不可用。匹配流程CheckCacheFile → TryMatchVarDescWithCache变量描述匹配的核心逻辑在CompareVarDesc()中遍历缓存中保存的所有变量描述与当前 VarManager 中的变量描述逐一比较GeTensorDesc包括 Format、DataType、Shape 等属性。全部一致才返回true。匹配成功后RefreshVariableDesc()会将缓存中的变量转换路TransRoad和 staged 描述刷新到当前 VarManager 中确保后续执行与缓存一致。5.4.3 模型加载变量描述匹配成功后执行模型加载反序列化LoadToGeRootModel()从.om文件加载模型数据通过ModelHelper::LoadRootModel()反序列化为GeRootModel外置权重处理AssignConstantVarMem()设置外置权重路径更新模型中常量节点的内存地址更新 Session IDUpdateGeModelSessionId()将模型中所有子图的 Session ID 更新为当前 Session 的 ID更新 Session Graph IDUpdateSessionGraphId()更新图的会话标识5.5 缓存写入TryCacheModel编译完成后如果缓存已启用且不在 debug 模式下执行缓存写入生成文件名GenerateCacheFile()生成带时间戳的文件名格式为{graph_key}_{timestamp}.om序列化模型SerializeModel()将GeRootModel序列化为二进制数据ModelBufferData通过ModelHelper::SaveToOmRootModel()完成保存到文件SaveModelToGeRootModel()将序列化后的数据通过FileSaver::SaveToFile()写入.om文件保存变量描述SaveVarDescToFile()将编译前后的变量描述信息序列化为 protobuf 格式写入.rdcpkt文件。具体包含desc_info_before_compile编译前的变量描述在TryLoadModelFromCache入口处通过GE_DISMISSABLE_GUARD记录desc_info_after_compile编译后的变量描述changed_var_names本次编译中发生变化的变量名列表staged_var_tensor_desc_mapstaged 状态的变量描述更新索引文件SaveCacheIndexFile()将新的缓存条目追加到.idx索引文件中5.6 并发安全模型缓存通过以下机制保证并发安全文件锁初始化时通过flock(LOCK_EX)对{graph_key}.lock加排他锁防止多进程并发读写同一graph_key的缓存索引追加新缓存条目采用追加方式写入索引文件而非覆盖即使同一graph_key也可以保留多条缓存记录通过变量描述匹配选择合适的一条5.7 变量变更控制GraphRebuildStateCtrlGraphRebuildStateCtrlcompiler/graph/manager/util/graph_rebuild_state_ctrl.h追踪变量的格式变更状态与模型缓存协同工作变更记录当变量格式发生变化时通过SetStateChanged()记录变更的变量名重编译判断IsGraphNeedRebuild()判断包含变更变量的图是否需要重新编译变更次数限制通过resource_names_to_change_times_限制每个变量最多变更kMaxVarChangeTimes_ 1次格式防止变量格式在多个状态间反复切换导致缓存不可用在模型缓存加载后RefreshVariableDesc()会调用var_accelerate_ctrl_-SetStateChanged()将从缓存恢复的变量变更状态通知给GraphRebuildStateCtrl确保后续的重编译判断正确。5.8 JIT 模式下的缓存机制CompiledModelCacheCompiledModelCacheapi/session/jit_execution/cache/compiled_model_cache.h在 JIT 执行模式下管理编译缓存其缓存根目录为{cache_dir}/jit/。初始化流程CompiledModelCache 构造函数: ├── 从上下文读取 user_graph_key 和 root_dir ├── 设置 root_dir_ root_dir /jit/ └── 创建目录缓存恢复RestoreCacheRestoreCache()恢复 ExecutionOrder 的断图结果包括读取slicing_result.json获取断图策略为每个 ExecutionPoint 恢复 GEP 信息对每个 GEP通过GuardedExecutionPointUtil::RestoreGuardedExecutionPoint()执行恢复gep_graph_key到当前线程上下文通过ModelCache::TryLoadModelFromCache()从缓存加载已编译的子图加载 guard check 函数缓存保存SaveCacheSaveCache()在编译完成后保存断图策略到slicing_result.json每个 ExecutionPoint 下所有 GEP 的gep_graph_key到gep_list.json子图序列化到slice_graph.pbGEP 级别的 graph_key 生成每个 GEP 的缓存通过自动生成的gep_graph_key区分gep_graph_key user_graph_key _ ep_id _ timestamp_ns这确保了即使同一 UserGraph 中的不同 GEP 也有唯一的缓存标识。5.9 ACL 算子模型缓存OpModelCacheOpModelCacheapi/acl/acl_op_executor/single_op/op_model_cache.h是一个纯内存的算子模型缓存不涉及磁盘持久化。存储结构unordered_mapuint64_t, OpModel以opModelId为键线程安全使用recursive_mutex保护所有读写操作功能Add()缓存已加载的算子模型GetOpModel()按 ID 查找已缓存的算子模型Delete()删除缓存并卸载算子资源CreateCachedExecutor()基于缓存创建 RT2 执行器CleanCachedModels()清空所有缓存6. 缓存文件结构完整的缓存目录结构如下{cache_dir}/ ├── cache.conf # 可选缓存配置 ├── {graph_key}.lock # 文件锁 ├── {graph_key}.idx # 索引文件JSON ├── {graph_key}_{timestamp}.om # 编译后的模型文件 ├── {graph_key}_{timestamp}.rdcpkt # 变量描述文件protobuf ├── weight/ # 外置权重目录如启用 │ └── {weight_files} └── jit/ # JIT 缓存目录 ├── slicing_result.json # 断图结果 └── {slice_graph_id}/ ├── gep_list.json # GEP 列表 ├── slice_graph.pb # 子图序列化 └── rem_graph.pb # 剩余图序列化7. 关键数据结构结构定义位置用途CacheFileIdxcompiler/graph/build/model_cache.h索引文件中的单条缓存记录包含 graph_key、om 文件路径、变量描述文件路径VarDescCachecompiler/graph/build/model_cache.h变量描述缓存包含变量描述映射、转换路、变更变量名列表CacheConfigcompiler/graph/build/model_cache.h缓存配置manual_check、debug_modeVarMatchInfoprotobuf 定义缓存中的变量匹配信息包含编译前后的变量描述VarDescInfoprotobuf 定义变量描述信息的序列化格式8. 注意事项缓存目录必须预先存在如果指定的ge.graph_compiler_cache_dir目录不存在Init()会返回PARAM_INVALID错误graph_key 的稳定性相同图结构应使用相同的graph_key不同图结构应使用不同的graph_key。用户需自行保证graph_key与图结构的对应关系变量变更导致缓存失效如果图中 Variable 算子的格式或 Shape 发生变化缓存会自动失效并触发重新编译外置权重路径一致性配合外置权重使用时第二次编译需确保权重文件在缓存目录的weight/子目录下可访问可通过ge.externalWeightDir指定持久化路径进程间互斥同一graph_key的缓存操作通过文件锁互斥同一时刻只有一个进程可以操作【免费下载链接】geGEGraph Engine是面向昇腾的图编译器和执行器提供了计算图优化、多流并行、内存复用和模型下沉等技术手段加速模型执行效率减少模型内存占用。 GE 提供对 PyTorch、TensorFlow 前端的友好接入能力并同时支持 onnx、pb 等主流模型格式的解析与编译。项目地址: https://gitcode.com/cann/ge创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考