
运行时执行系统——从 OM 文件到硅片计算的桥梁【免费下载链接】geGEGraph Engine是面向昇腾的图编译器和执行器提供了计算图优化、多流并行、内存复用和模型下沉等技术手段加速模型执行效率减少模型内存占用。 GE 提供对 PyTorch、TensorFlow 前端的友好接入能力并同时支持 onnx、pb 等主流模型格式的解析与编译。项目地址: https://gitcode.com/cann/ge介绍 GE 运行时如何完成从模型加载到任务下沉再到结果回传的完整执行闭环。1. 全局架构双版本并存的设计GE 运行时同时维护 v1 和 v2 两套执行架构这是一种有意识的演进策略。1.1 v1 架构静态 shape 执行器v1 是当前的生产主力承担着静态 shape 模型加载与执行职责。其核心目录结构为runtime/v1/ ├── graph/ │ ├── load/ # 模型加载入口 │ │ ├── graph_loader.cc # 加载门面Facade 模式 │ │ └── model_manager/ # 模型管理核心 │ │ ├── model_manager.cc # 全局单例模型注册表 │ │ ├── davinci_model.cc # 单个模型实例核心 │ │ └── task_info/ # 各类 Task 的分发实现 │ ├── execute/ # 模型执行入口 │ │ ├── graph_executor.cc # 同步/异步执行门面 │ │ └── model_executor.cc # Session 级执行协调器 │ └── manager/ # 内存管理 │ ├── mem_manager.cc │ ├── caching_allocator.cc │ └── session_scope_mem_allocator.cc ├── hybrid/ # 动态 shape 混合执行模式 ├── single_op/ # 单算子执行模式 └── opskernel_executor/ # 算子内核执行器1.2 v2 架构动态 shape 执行器v2 是下一代运行时设计目标是通过Lowering将 ComputeGraph 转换为 ExecuteGraph实现更精简的执行路径runtime/v2/ ├── core/ │ ├── model_v2_executor.cc # v2 模型执行器 │ ├── stream_executor.cc # 流级执行器管理 │ └── executor/ # 多种执行策略 │ ├── sequential/ # 顺序执行C 语言实现 │ ├── topological/ # 拓扑排序执行 │ └── multi_thread_topological/ # 多线程拓扑执行 ├── lowering/ # ComputeGraph → ExecuteGraph 转换 ├── engine/ # 引擎适配层aicore/aicpu/dvpp... └── kernel/ # 内核注册与执行1.3 v1 与 v2 的架构差异维度v1v2核心抽象DavinciModel TaskDefExecuteGraph Node/Kernel执行模型rtModelExecute硬件 SinkHost 顺序/拓扑执行适用场景静态 shape 模型Sink 模式动态 shape / 单算子内存管理分段式FM/Weight/Var统一 Allocator代码语言C重运行时C 核心 C kernel追求极致性能v1 保障存量 OM 模型的兼容性v2 为动态 shape 场景提供更灵活的基础设施。2. v1 模型加载从 OM 到设备的映射2.1 加载流程总览DavinciModel 是 v1 的核心通过六阶段流水线完成内存映射、I/O 初始化、算子初始化和任务下沉。2.2 GraphLoader极简门面GraphLoaderruntime/v1/graph/load/graph_loader.cc是一个纯粹的Facade 模式——所有方法都是静态方法直接转发给ModelManager::GetInstance()。这种设计有个好处解耦加载协议与内部实现上层Session、ACL只需知道加载一个模型不需要知道 ModelManager 的存在。关键加载入口LoadModelOnline在线模式直接从内存中的 GeRootModel 加载LoadModelFromData离线模式从序列化的 ModelData 加载LoadModelWithQ队列模式绑定输入/输出队列用于数据流场景2.3 ModelManager全局模型注册表ModelManagerruntime/v1/graph/load/model_manager/model_manager.h是一个进程级单例维护两个并行的模型注册表model_map_: mapuint32_t, shared_ptrDavinciModel # 静态/已知 shape 模型 hybrid_model_map_: mapuint32_t, shared_ptrHybridDavinciModel # 动态 shape 模型两种模型的执行路径完全不同——DavinciModel 走rtModelExecute硬件 SinkHybridDavinciModel 走 Host 端的子图调度。分开存储使每种路径都可以做到零开销抽象。ModelManager 的职责AICPU Kernel 生命周期管理加载/卸载自定义 AICPU SO 库权重共享通过weights_mem_ids_to_addr_info_支持多模型共享同一份权重内存Session 绑定sess_id_to_device_ids_跟踪 Session 与设备的映射关系资源回收在模型卸载时清理所有运行时资源2.4 DavinciModel模型的运行时化身DavinciModelruntime/v1/graph/load/model_manager/davinci_model.h是 v1 运行时的绝对核心——它将编译产物GeModel转化为可执行状态并管理整个执行生命周期。2.4.1 初始化流程DavinciModel 的 Init 过程是一个精心编排的六阶段流水线InitModelMem → InitIoNodes → TransAllVarData → InitNodes → DoTaskSink → ...阶段一InitModelMem—— 内存映射GE 有三种设备内存需要管理Feature Map 内存mem_base_算子输入/输出的工作区对应编译期计算的runtime_param_.mem_size权重内存weights_mem_base_模型参数Constant、Variable 的初始值变量内存var_mem_base_训练场景下的可变参数内存来源有两种GE 自行分配MallocFeatureMapMem→aclrtMalloc外部提供用户通过SetFeatureMemoryBase设定在多模型共部署场景下用户可能希望统一管理设备内存避免 GE 内部分配导致的内存碎片化。阶段二InitIoNodes—— I/O 节点初始化遍历 Data 和 NetOutput 节点建立输入/输出的地址映射。核心是Zero-Copy机制用户 Tensor 地址 → ZeroCopyOffset → 模型内部 Args 地址Zero-Copy 通过直接将用户 Tensor 地址写入模型内部的 Args 表消除了输入/输出数据的额外拷贝。阶段三InitNodes—— 算子节点初始化对每个节点执行特定引擎的初始化TBE 算子注册 Kernel HandleInitTbeHandleHCCL 算子收集通信流信息LabelSet/StreamSwitch控制流硬件资源分配阶段四DoTaskSink—— 任务下沉核心这是 Sink 模式的核心实现BindModelStream将所有逻辑流绑定到 rtModel 句柄。在昇腾硬件上一个 rtModel 包含多个 rtStream每个 Stream 上的 Task 可以并行执行。InitTaskInfo DistributeTask遍历 ModelTaskDef 中的所有 TaskDefKernel、Hccl、FftsPlus 等为每个 Task 创建对应的 TaskInfo 对象并调用Distribute()下发到设备。aclmdlRIBuildEnd通知底层运行时模型构建完毕此后模型可以被rtModelExecute执行。TaskSink 将所有 Task 预加载到设备Host 只需一次rtModelExecute调用。对于有上千个算子的模型这避免了 Host 调度成为瓶颈。3. v1 模型执行Sink 模式的两种触发方式Sink 模式是 GE 的核心运行时优化机制。传统 Host 调度: Host 逐算子下发 → Device 执行 → Host 下发下一个 → ...N 次交互 Sink 模式: Host 一次 launch → Device 自主执行所有 Task1 次交互GE 的 TaskSink 在编译期将 Task 序列化到 OM 文件中运行时只需一次调用即可触发设备端全部 Task 执行。3.1 两种执行方式GE v1 的 Sink 模式支持两种触发方式对应不同的使用场景3.2 Run 循环模式独立线程常驻执行DavinciModel::Run()在独立线程中循环执行循环 { 1. 从 DataInputer 队列中取出输入数据 2. HandleInputData: 将输入地址写入模型的 Args 表 3. rtModelExecute: 一次调用触发设备端全部 Task 的执行 4. rtStreamSynchronizeWithTimeout: 等待设备完成 5. AssembleListenerOutput: 组装输出 6. 回调通知完成 }关键设计决策独立线程ModelRunStart()创建一个专用线程执行 Run 循环。模型是常驻的——加载后持续接收数据并执行直到ModelRunStop()。线程与 DataInputer 队列配合实现了生产者-消费者模式。超时机制rtStreamSynchronizeWithTimeout支持配置超时时间。超时后会调用aclmdlRIAbort中止模型执行避免设备死锁。错误传播设备端错误通过rtStreamSynchronizeWithTimeout的返回值传播到 Host。特殊的返回码如kSinkModelEndOfSequence序列结束和kSinkModelAbortNormal正常中止有特定含义。GE 的 TaskSink 在编译期就已经将 Task 序列化到 OM 文件中运行时通过rtModelExecute一次调用即可触发设备端全部 Task 的执行。3.3 NnExecute 模式调用者线程按需执行DavinciModel::NnExecute()是调用者线程主动触发的执行方式每次推理由调用方同步或异步发起NnExecute(stream, async_mode, input_tensor, output_tensor): 1. InitModelStream: 初始化或复用执行流 2. CopyModelData: 将输入 Tensor 地址映射到模型 ArgsZero-Copy或拷贝 3. rtModelExecute: 下发模型执行 4. rtStreamSynchronizeWithTimeout: 等待完成如果非异步 5. CopyOutputData: 从模型内部地址拷贝输出到用户 Tensor 6. UpdateOutputTensorShape: 如果是动态 shape更新输出 shape当使用 forbidden 流且设置了超时时间时会调用rtModelExecuteSync接口其内部会做流同步超时会 abort model。Run 循环模式 vs NnExecute 模式的差异维度Run 循环模式NnExecute 模式触发方式独立线程循环从队列取数据调用者线程主动调用线程模型独立线程调用者线程数据拷贝通过 DataInputer 队列传递CopyModelData CopyOutputData适用场景高吞吐推理服务交互式推理/小批量两种方式的底层执行都依赖rtModelExecute属于 Sink 模式的不同使用形态。3.4 API 入口与内部执行函数的对应关系外部 API 通过不同路径最终到达Run()或NnExecute()┌──────────────────────────────────────────────────────────────┐ │ API 入口层 │ ├────────────────────┬─────────────────────────────────────────┤ │ ACL 层 │ GE Session 层 │ │ aclmdlExecuteV2 │ GeSession::RunGraph │ │ aclmdlExecuteAsyncV2 │ GeSession::RunGraphAsync │ │ │ GeSession::RunGraphAsyncWithStream │ └─────────┬──────────┴──────────────┬──────────────────────────┘ │ │ ▼ ▼ NnExecute() Run() 或 NnExecute() (调用者线程) (取决于执行路径)走向NnExecute()的入口API调用链说明aclmdlExecuteV2GeExecutor::ExecModel→GraphLoader::ExecuteModel→ModelManager::ExecuteModel→NnExecute同步执行调用者线程阻塞等待aclmdlExecuteAsyncV2GeExecutor::ExecModel(async_modetrue)→ModelManager::ExecuteModel→NnExecute(async_modetrue)异步执行GeSession::RunGraphGraphManager::RunGraph→ModelExecutor::RunGraph→GraphExecutor::ExecuteGraph→ModelManager::syncExecuteModel→NnExecute注意同步路径实际走 NnExecute而非 RunGeSession::RunGraphAsyncWithStreamModelExecutor::ExecuteGraphWithStream→ModelManager::ExecuteModelWithStreamAsync→NnExecute带指定 Stream 的异步执行走向Run()的入口API调用链说明GeSession::RunGraphAsyncGraphManager::RunGraphAsync→ 队列推送 →ModelExecutor::RunThread→GraphExecutor::ExecuteGraphAsync→ModelManager::DataInputTensor→model-Push(args)→data_inputer_队列 →Run()从队列 Pop 执行唯一真正走 Run 循环的入口一个值得注意的设计细节GeSession::RunGraph虽然名称暗示运行图但实际走的是NnExecute而非Run()循环。真正通过data_inputer_队列驱动Run()后台线程的只有GeSession::RunGraphAsync。3.5 GraphExecutor 与 ModelExecutor 的分工GraphExecutorruntime/v1/graph/execute/graph_executor.cc和ModelExecutorruntime/v1/graph/execute/model_executor.cc构成了执行层的双层抽象GraphExecutor—— 纯粹的执行代理所有方法都是静态或 const 方法不持有状态直接转发到ModelManager职责同步执行ExecuteGraph、异步执行ExecuteGraphAsync、流级执行ExecuteGraphWithStreamModelExecutor—— Session 级的执行协调器继承自Executor基类持有GraphNode注册表graph_nodes_管理资源回收内存、流、事件支持异步执行线程RunThreadrun_args_q_ModelExecutor 需要处理加载决策——在资源不足时需要卸载已有模型腾出空间。这个逻辑与纯执行逻辑解耦使得 GraphExecutor 可以专注于执行路径。3.5.1 资源回收策略ModelExecutor::CheckAndReleaseMemory展示了一种优雅降级策略1. 检查空闲内存是否足够加载新模型 2. 如果不够遍历所有已加载的模型 3. 对每个模型检查是否包含 HCCL Task通信算子不可卸载 4. 如果不包含卸载该模型释放资源 5. 重新检查空闲内存 6. 如果仍不够继续卸载下一个模型同样的逻辑也用于流CheckAndReleaseStream和事件CheckAndReleaseEvent资源的回收。这种设计确保了在设备资源受限的情况下系统仍然能够通过以旧换新的方式运行新模型。HCCLHuawei Collective Communication Library涉及跨设备通信卸载会破坏通信拓扑的完整性。这是分布式训练场景下的重要约束。4. v2 架构基于 Lowering 的新一代运行时4.1 设计哲学编译即执行准备v2 的核心思想是Lowering——将高层 ComputeGraph 转换为底层 ExecuteGraph使得运行时只需要一个极其简单的执行循环。4.2 Lowering从 ComputeGraph 到 ExecuteGraphGraphConverter::ConvertComputeGraphToExecuteGraphruntime/v2/lowering/graph_converter.cc是 v2 的核心转换流程Lowering 的核心步骤Init Graph 生成将所有初始化操作常量加载、流分配、内存分配器创建提取到一个独立的 Init 子图中。Main Graph 生成对每个 ComputeGraph 节点查找对应的NodeConverter通过NodeConverterRegistry调用其 lowering 函数生成一个或多个 ExecuteGraph 节点。事件同步LoweringEventSync处理跨流的 Send/Wait 事件同步。优化OfflineOptimizer对生成的 ExecuteGraph 进行优化如常量折叠、死代码消除。v1 在运行时通过DistributeTask将 Task 逐个下发到设备而 v2 在编译期就已经将 ComputeGraph 转换为可以直接执行的 ExecuteGraph。运行时不需要任何翻译步骤。4.3 ModelV2Executor三阶段生命周期ModelV2Executorruntime/v2/core/model_v2_executor.cc管理三个子图的生命周期Init Graph → Main Graph → DeInit GraphLoad 阶段加载并执行 Init Graph内存分配、流分配、常量初始化卸载 Init Graph加载 Main Graph不执行仅准备执行数据Execute 阶段指定输入/输出 Tensor指定运行时参数流、事件、通知、内存分配器执行 Main GraphUnLoad 阶段卸载 Main Graph加载并执行 DeInit Graph资源清理卸载 DeInit Graphv2 的执行器是纯 C 实现的顺序执行循环无法处理分配内存这种需要与运行时 API 交互的操作。将这些操作提取到 Init/DeInit 子图中可以保持 Main Graph 的纯粹性——Main Graph 只包含纯计算节点。4.4 Sequential Executor极致简洁的执行引擎v2 的核心执行引擎runtime/v2/core/executor/sequential/executor/sequential_executor.c采用极简的执行循环KernelStatus SequentialExecute(void *arg) { SequentialExecutionData *execution_data (SequentialExecutionData *)arg; for (size_t i 0U; i execution_data-node_num; i) { Node *node execution_data-nodes[i]; KernelStatus ret node-func((node-context)); if (ret ! kStatusSuccess) { return ret; } } return kStatusSuccess; }使用 C 实现的原因无运行时开销C 语言没有异常处理、RTTI、虚函数表等隐式开销。这个循环在每次推理中执行成千上万次每纳秒都 counts。可移植性C 代码可以直接在设备端AICORE 的 DSP执行为未来的设备端调度留下空间。SequentialExecutionData的结构极其精简typedef struct { size_t node_num; Node **nodes; // 节点数组预排序 size_t input_num; AsyncAnyValue **input_values; // 输入值由外部设定 size_t output_num; AsyncAnyValue **output_values; // 输出值由外部设定 } SequentialExecutionData;每个 Node 只包含节点 ID、执行函数指针、运行上下文。这种扁平化设计消除了虚函数调用、指针追逐等开销。4.5 StreamExecutor多流并发管理StreamExecutorruntime/v2/core/stream_executor.cc为每个 ACL Stream 创建独立的 ModelV2Executor 实例streams_to_executor_: mapaclrtStream, unique_ptrModelV2Executor每个 Stream 一个 Executor 是因为在异步执行模式下多个 Stream 可能并发执行同一个模型的不同推理请求。每个 Executor 维护自己的执行状态输入/输出绑定、迭代计数等互不干扰。这与昇腾 Stream 的设计理念一致——Stream 是设备端操作的有序队列不同 Stream 之间可以并行。5. Hybrid 执行动态 Shape 的解决方案5.1 为什么需要 Hybrid静态 shape 模型的 TaskSink 路径虽然高效但面对动态 shape如 NLP 中的变长序列就无能为力——因为不同的 shape 对应不同的 Task 序列和内存布局。HybridDavinciModelruntime/v1/hybrid/hybrid_davinci_model.h是动态 shape 场景的执行入口。它的存在由ModelManager::IsNeedHybridLoad判断——当GeRootModel被标记为动态 shape 时走 Hybrid 路径。5.2 Hybrid 执行架构Hybrid 执行经历了从 v1 到 v2 的演进。v1 的HybridModelRtV1Executor基于SubgraphExecutor、NodeDoneManager和ShapeInferenceEngine构建采用 Host 端逐算子调度的方式该路径已不再演进。当前 Hybrid 场景的主力执行器是HybridModelRtV2Executor它复用了 v2 运行时的ExecuteGraph基础设施。HybridModelRtV2Executor 的执行流程初始化通过RtV2ExecutorFactory::Create创建执行器。如果图包含PartitionedCall节点Stage 分区则创建RtV2PipelineExecutor否则创建RtV2SimpleExecutor。Lowering将GeRootModel中的ComputeGraph通过ModelConverter转换为ExecuteGraph。这一步在编译期已完成运行时直接加载。执行ModelV2Executor管理 Init/Main/DeInit 三个子图的生命周期Main Graph 通过SequentialExecutor或TopologicalExecutor执行。与 v1 的逐算子 Host 调度不同v2 的 Hybrid 执行器将整图转换为ExecuteGraph后运行时只需执行极简的节点循环大幅降低了 Host 侧的调度开销。6. 单算子执行模式6.1 设计背景单算子模式最初是为了支持 PyTorch 在昇腾设备上运行而引入的。PyTorch 早期采用动态图执行模型每次只执行单个算子。为了让 PyTorch 能够利用 GE 的编译能力GE 引入了单算子执行模式当 PyTorch 调用aclopCompileAndExecuteV2接口时GE 会为这个单算子构造 Data 和 NetOutput 节点组成一张最小化的图然后进行编译和执行。6.2 当前状态目前 PyTorch 主要使用aclnn作为单算子执行路径只有不支持aclnn的算子才会走aclop这条线。相应地GE 中的single_op模块也已不再演进。SingleOpruntime/v1/single_op/single_op.h和DynamicSingleOp提供了算子级执行能力SingleOp固定 shape 的单算子执行。初始化时确定 shape后续执行无需重新编译。DynamicSingleOp动态 shape 的单算子执行。每次执行可能传入不同的 shape。7. Session 管理模型的生命周期上下文7.1 Session 层次结构7.2 InnerSessionSession 的核心实现InnerSessionapi/session/session/inner_session.h是每个用户 Session 的完整上下文包含GraphManager管理图的编译AddGraph → BuildGraph → CompileGraphModelExecutor管理图的加载和执行LoadGraph → RunGraphSession ID全局唯一标识用于变量管理、内存隔离InnerSession 的生命周期Initialize() → AddGraph() → BuildGraph() / CompileGraph() → RunGraph() → Finalize()关键设计决策编译与执行分离BuildGraph只编译不加载RunGraph会触发首次加载和执行。这种延迟加载策略避免了不必要的设备资源占用。外部内存管理SetGraphConstMemoryBase/UpdateGraphFeatureMemoryBase允许用户自行管理设备内存GE 只负责在用户提供的内存上构建模型。ForkGraphinner_session.h支持 fork 一个已编译的图fork 出的图共享编译产物但可以独立加载和执行。这是多实例并发推理的关键能力——避免重复编译。8. 内存管理分段式策略8.1 内存分区模型GE 将设备内存分为多个逻辑段┌─────────────────────────────────────────────────────┐ │ Device Memory │ ├──────────┬──────────────┬──────────┬────────────────┤ │ Weights │ Feature Map │ Variable │ Zero-Copy IO │ │ (固定) │ (算子激活内存)│ (训练) │ (模型输入输出) │ ├──────────┼──────────────┼──────────┴────────────────┤ │ Fixed FM │ Refreshable FM│ │ │ (不可刷新)│ (可刷新) │ │ └──────────┴──────────────┴───────────────────────────┘8. 多流并行GE 的多流并行算法基于图的拓扑结构和引擎类型为每个节点分配执行引擎基于拓扑和引擎为每个节点分配 Stream不同 Stream 间插入同步保证执行时序三种并行场景计算与通信并行AllReduce 和 Convolution 无依赖时可并发不同引擎并行AI Core 和 DVPP 可同时工作同引擎内并行一个算子无法占满引擎时不同拓扑集合可并发9. 运行时设计特点维度GE Runtime v1GE Runtime v2执行模型TaskSink Host调度Host顺序/拓扑执行动态 ShapeHybrid 子图调度不再演进ExecuteGraph 节点级内存管理分段式 Zero-Copy统一 Allocator多流并行多 rtStream 绑定 rtModel多 Executor 实例加载/执行分离是Load NnExecute是Load ExecuteGE 运行时的独特之处TaskSink 模式将整个执行序列预加载到设备Host 零调度开销。这是昇腾硬件的特色能力——设备端的 Task 调度器可以自主执行预加载的 Task 序列。双版本运行时v1 追求极致的静态性能Sink 模式v2 追求灵活性和可扩展性Lowering 纯 C 执行器。多引擎异构执行AICore、AICPU、DVPP、HCCE、HostCPU 等引擎在同一个运行时中协同工作。运行时系统将编译器产出的静态执行计划映射到物理设备在 Sink 模式下实现了极致的执行效率但在动态 shape 场景下仍需承受 Host 调度的开销——这自然引出对任务序列优化、流并行调度、内存复用等关键优化技术的需求。【免费下载链接】geGEGraph Engine是面向昇腾的图编译器和执行器提供了计算图优化、多流并行、内存复用和模型下沉等技术手段加速模型执行效率减少模型内存占用。 GE 提供对 PyTorch、TensorFlow 前端的友好接入能力并同时支持 onnx、pb 等主流模型格式的解析与编译。项目地址: https://gitcode.com/cann/ge创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考