GE 图引擎分引擎模块分析
GE 图引擎分引擎模块分析
一、问题背景:为什么需要引擎分区?
GE 图引擎面临一个根本性的架构挑战:如何在一张图中同时处理不同引擎的算子?
1.1 具体场景
典型问题场景:
- 用户模型中包含多种引擎的算子:
- AiCore引擎:高性能计算算子(如
Conv2D、MatMul) - AiVector引擎:向量计算算子(如
Add、Mul) - Host CPU引擎:Host端计算算子(如
Dynamic算子) - Custom引擎:用户自定义算子
- AiCore引擎:高性能计算算子(如
- 不同引擎的算子有不同的执行特性:
- AiCore/AiVector:Device端执行,需要流分配
- Host CPU:Host端执行,需要Host调度
- Custom:可能依赖特定引擎
- 如果不分区,会导致:
- 流分配混乱(不同引擎需要不同流)
- 调度冲突(Host和Device算子调度方式不同)
- 性能下降(无法做引擎级别的优化)
1.2 现有方案的不足
| 方案 | 问题 |
|---|---|
| 单引擎执行 | 无法支持多引擎算子,功能受限 |
| 用户手动分区 | 用户负担重,破坏模型可移植性 |
| 算子级别调度 | 调度开销大,无法做跨算子优化 |
| 忽略引擎差异 | 流分配和调度混乱,性能下降 |
1.3 GE 的解决方案
设计一套自动化的引擎分区机制,将一张混合图按引擎拆分成多个子图:
- 引擎子图:每个子图包含同一引擎的算子
- 自动连接:通过
PlaceHolder和End算子连接子图 - 流分配优化:每个引擎子图可以独立分配流
- 调度优化:不同引擎子图可以采用不同的调度策略
核心价值:用户无需关心引擎分区细节,系统自动适配,既保证正确性,又最大化性能。
二、设计哲学:引擎隔离,流独立,自动连接
GE 的引擎分区遵循一个清晰的设计哲学:**”同引擎算子同子图,异引擎算子异子图,自动连接不丢数据”**。
这个哲学体现在三个核心原则:
2.1 原则一:引擎隔离
动机:不同引擎的算子有不同的执行特性,应隔离到不同子图。
实现:
- 每个节点通过
EnginePlacer分配引擎 - 同引擎的节点合并到同一 Cluster
- 每个 Cluster 对应一个子图
代码依据:engine_place.cc:44-85(SelectEngine)
2.2 原则二:流独立
动机:不同引擎的算子需要不同的流,应独立分配。
实现:
- 每个子图可以独立分配流(通过
stream_label_) - 同引擎子图可以复用流
- 异引擎子图必须分配不同流
代码依据:engine_partitioner.h:42-43(Cluster::stream_label_)
2.3 原则三:自动连接
动机:分区后数据流不能断,必须保证输入输出正确。
实现:
- 异引擎子图之间通过
PlaceHolder和End算子连接 PlaceHolder:占位符,表示来自其他子图的输入End:结束符,表示输出到其他子图- 合并时自动移除
PlaceHolder和End,恢复原边
代码依据:engine_partitioner.cc:636-682(AddPlaceHolderEndInSrcDstGraph)
三、核心数据结构
3.1 Cluster:分区的基本单元
1 | |
设计亮点:
index_记录拓扑位置,用于判断是否可以合并(避免环)in_clu_和out_clu_记录 Cluster 间的连接关系engine_name_和stream_label_用于分区和流分配
3.2 EnginePlacer:引擎分配器
1 | |
核心职责:
- 为每个节点选择引擎(
SelectEngine) - 多线程执行引擎选择(
Run) - 分配复合引擎(
AssignCompositeEngine) - 重新分配引擎(
ReAssignEngine)
3.3 EnginePartitioner:分区器
1 | |
核心职责:
- 初始化 Cluster(
Initialize) - 标记 Cluster(
MarkClusters) - 拆分子图(
SplitSubGraphs) - 合并子图(
MergeAfterSubGraphOptimization)
3.4 DNNEngineManager:引擎管理器
1 | |
核心职责:
- 管理所有引擎(
GetEngine) - 获取引擎名称(
GetDNNEngineName) - 获取复合引擎名称(
GetCompositeEngineName) - 获取排除的引擎(
GetExcludeEngines)
四、核心流程
4.1 整体流程架构
flowchart TD
Start([图预处理入口]) --> CheckGraph{检查图有效性}
CheckGraph -->|无效| ReturnFailed[返回失败]
CheckGraph -->|有效| TopoSort[拓扑排序]
TopoSort --> CheckMode{分区模式}
CheckMode -->|复合引擎分区| AssignComposite[AssignCompositeEngine<br/>分配复合引擎]
CheckMode -->|原子引擎分区| InitPartition[初始化分区信息]
CheckMode -->|二次分区| InitPartition
CheckMode -->|合并模式| Merge[MergeSubGraph<br/>合并子图]
AssignComposite --> InitPartition
InitPartition --> ClearData[ClearAllPartitionData<br/>清理分区数据]
ClearData --> PartitionSubGraph[PartitionSubGraph<br/>分区子图]
PartitionSubGraph --> Initialize[Initialize<br/>初始化 Cluster]
Initialize --> EnginePlacerRun[EnginePlacer::Run<br/>多线程分配引擎]
EnginePlacerRun --> SelectEngine[SelectEngine<br/>选择引擎]
SelectEngine --> CheckEngineAttr{检查引擎属性}
CheckEngineAttr -->|已有引擎属性| UpdateOpDesc[UpdateOpdescWithAttr<br/>更新 OpDesc]
CheckEngineAttr -->|无引擎属性| GetEngineName[GetDNNEngineName<br/>获取引擎名称]
GetEngineName --> CheckSupport{检查算子支持}
CheckSupport -->|不支持| ReturnFailed
CheckSupport -->|支持| RecordEngine[记录引擎映射<br/>node_atomic_engine_map_]
UpdateOpDesc --> RecordEngine
RecordEngine --> CreateCluster[创建 Cluster<br/>每个节点一个 Cluster]
CreateCluster --> InitInputClusters[InitializeInputClusters<br/>初始化输入 Cluster]
InitInputClusters --> MarkClusters[MarkClusters<br/>标记 Cluster]
MarkClusters --> CheckTopoMode{拓扑排序模式}
CheckTopoMode -->|stable_rdfs_sort| MarkConsistantId[MarkClustersWithConsistantId<br/>保持 ID 一致性]
CheckTopoMode -->|其他| MarkNormal[MarkClusters<br/>常规标记]
MarkConsistantId --> SplitSubGraphs[SplitSubGraphs<br/>拆分子图]
MarkNormal --> SplitSubGraphs
SplitSubGraphs --> CreateSubGraph[创建子图<br/>每个 Cluster 一个子图]
CreateSubGraph --> SplitNodeInputs[SplitNodeInputs<br/>拆分节点输入]
SplitNodeInputs --> CheckCluster{检查 Cluster}
CheckCluster -->|同 Cluster| AddEdge[AddEdge<br/>添加边]
CheckCluster -->|异 Cluster| AddPlaceHolderEnd[AddPlaceHolderEnd<br/>添加 PlaceHolder/End]
AddEdge --> NextNode{还有节点?}
AddPlaceHolderEnd --> NextNode
NextNode -->|是| SplitNodeInputs
NextNode -->|否| SortSubGraphs[SortSubGraphs<br/>拓扑排序子图]
SortSubGraphs --> AddPartitions[AddPartitionsToGraphNode<br/>添加分区到图节点]
AddPartitions --> CheckSubGraph{检查子图}
CheckSubGraph -->|有子图| PartitionSubGraph
CheckSubGraph -->|无子图| End([分区完成])
Merge --> RemoveEndPld[RemoveNodeAndEdgeBetweenEndPld<br/>移除 PlaceHolder/End]
RemoveEndPld --> MergeAllSubGraph[MergeAllSubGraph<br/>合并所有子图]
MergeAllSubGraph --> InheritAttr[InheritOriginalAttr<br/>继承原图属性]
InheritAttr --> EnginePlacerRun2[EnginePlacer::Run<br/>重新分配引擎]
EnginePlacerRun2 --> End
ReturnFailed --> End
style Start fill:#e1f5e1
style End fill:#ffe1e1
style PartitionSubGraph fill:#e1f0ff
style MarkClusters fill:#f0e1ff
style SplitSubGraphs fill:#fff4e1
style Merge fill:#f5f5f5
4.2 引擎选择流程
sequenceDiagram
participant EnginePlacer as EnginePlacer
participant ThreadPool as ThreadPool<br/>(16线程)
participant Node as Node
participant OpDesc as OpDesc
participant DNNEngineManager as DNNEngineManager
participant OpInfo as OpInfo
EnginePlacer->>ThreadPool: 创建线程池
EnginePlacer->>EnginePlacer: 获取 exclude_engines
loop 每个节点(并行)
ThreadPool->>Node: GetOpDesc()
Node-->>ThreadPool: op_desc
ThreadPool->>OpDesc: 检查 ATTR_ENGINE_NAME_FOR_LX
OpDesc-->>ThreadPool: engine_name
alt 已有引擎属性
ThreadPool->>EnginePlacer: UpdateOpdescWithAttr
Note right of EnginePlacer: 更新 OpDesc 的引擎属性
else 无引擎属性
ThreadPool->>DNNEngineManager: GetDNNEngineName(node, exclude_engines)
Note right of DNNEngineManager: 调用算子注册信息<br/>检查算子支持
DNNEngineManager->>OpInfo: 创建 matched_op_info
OpInfo-->>DNNEngineManager: op_info
DNNEngineManager-->>ThreadPool: selected_engine_name
alt 算子不支持
ThreadPool->>ThreadPool: is_check_support_success = false
ThreadPool-->>EnginePlacer: FAILED
else 算子支持
ThreadPool->>OpDesc: SetOpEngineName(engine_name)
end
end
ThreadPool->>EnginePlacer: node_atomic_engine_map_.emplace(node, engine_name)
Note right of EnginePlacer: 记录节点-引擎映射
end
ThreadPool-->>EnginePlacer: 所有线程完成
EnginePlacer->>DNNEngineManager: UpdateOpDescsWithOpInfos(nodes_op_infos)
Note right of DNNEngineManager: 更新所有 OpDesc 的 OpInfo
EnginePlacer-->>EnginePlacer: 引擎分配完成
4.3 Cluster 合并流程
flowchart TD
Start([开始合并 Cluster]) --> LoopCluster[遍历所有 Cluster]
LoopCluster --> GetChildCluster[获取 child_cluster]
GetChildCluster --> GetParents[获取所有 parent_cluster]
GetParents --> SortParents[按输出数量排序<br/>输出少的优先合并]
SortParents --> LoopParent[遍历 parent_cluster]
LoopParent --> CheckMergeable{是否可合并?}
CheckMergeable --> CheckEngine{引擎相同?}
CheckEngine -->|不同| NotMerge[不可合并]
CheckEngine -->|相同| CheckStream{流标签相同?}
CheckStream -->|不同| NotMerge
CheckStream -->|相同| RemoveEdge[RemoveEdge<br/>移除父子边]
RemoveEdge --> CheckSecondPath{HasSecondPath<br/>检查第二路径}
CheckSecondPath -->|有第二路径| InsertEdgeBack[InsertEdge<br/>恢复父子边]
CheckSecondPath -->|无第二路径| InsertEdgeBack
InsertEdgeBack -->|有第二路径| NotMerge
InsertEdgeBack -->|无第二路径| MergeTwoClusters[MergeTwoClusters<br/>合并两个 Cluster]
MergeTwoClusters --> UpdateNodeMap[更新 node_2_cluster_]
UpdateNodeMap --> MergeNodes[合并 nodes_]
MergeNodes --> MergeInOut[合并 in_clu_ 和 out_clu_]
MergeInOut --> UpdateInOutClusters[更新其他 Cluster 的输入输出]
UpdateInOutClusters --> NextParent{还有 parent?}
NotMerge --> NextParent
NextParent -->|是| LoopParent
NextParent -->|否| NextChild{还有 child?}
NextChild -->|是| GetChildCluster
NextChild -->|否| End([合并完成])
style Start fill:#e1f5e1
style End fill:#ffe1e1
style MergeTwoClusters fill:#e1f0ff
style CheckSecondPath fill:#fff4e1
4.4 PlaceHolder/End 添加流程
sequenceDiagram
participant Partitioner as EnginePartitioner
participant SrcGraph as SrcGraph<br/>(源子图)
participant DstGraph as DstGraph<br/>(目标子图)
participant SrcNode as SrcNode<br/>(源节点)
participant DstNode as DstNode<br/>(目标节点)
participant EndNode as EndNode
participant PlaceHolderNode as PlaceHolderNode
Partitioner->>SrcNode: GetOutAnchor()
SrcNode-->>Partitioner: out_anchor
Partitioner->>DstNode: GetInAnchor()
DstNode-->>Partitioner: in_anchor
Partitioner->>Partitioner: 检查 Cluster
Note right of Partitioner: parent_cluster != child_cluster
Partitioner->>DstGraph: MakeEndOpNode()
Note right of DstGraph: 创建 End 算子
DstGraph->>EndNode: new EndNode
DstGraph-->>Partitioner: new_end_node
Partitioner->>Partitioner: UpdateEndOpDesc()
Note right of Partitioner: 更新 End 的输入描述
Partitioner->>Partitioner: SetEndOpAttr()
Note right of Partitioner: 设置 End 属性<br/>peerIndex、parentOpType等
Partitioner->>SrcNode: GraphUtils::AddEdge(out_anchor, end_in_anchor)
Note right of SrcNode: 连接源节点到 End
Partitioner->>SrcGraph: MakePldOpNode()
Note right of SrcGraph: 创建 PlaceHolder 算子
SrcGraph->>PlaceHolderNode: new PlaceHolderNode
SrcGraph-->>Partitioner: new_pld_node
Partitioner->>Partitioner: UpdatePldOpDesc()
Note right of Partitioner: 更新 PlaceHolder 的输出描述
Partitioner->>Partitioner: SetPldOpAttr()
Note right of Partitioner: 设置 PlaceHolder 属性<br/>peerIndex、parentOpType等
Partitioner->>DstNode: GraphUtils::AddEdge(pld_out_anchor, in_anchor)
Note right of DstNode: 连接 PlaceHolder 到目标节点
Partitioner->>Partitioner: 记录映射
Note right of Partitioner: end_2_pld_[end] = pld<br/>pld_2_end_[pld] = end<br/>index_2_end_[index] = end
Partitioner-->>Partitioner: PlaceHolder/End 添加完成
4.5 子图合并流程
sequenceDiagram
participant Partitioner as EnginePartitioner
participant OriginalGraph as OriginalGraph<br/>(原图)
participant SubGraphList as SubGraphList<br/>(子图列表)
participant MergedGraph as MergedGraph<br/>(合并图)
participant EndNode as EndNode
participant PlaceHolderNode as PlaceHolderNode
Partitioner->>OriginalGraph: GetAllSubgraphs()
OriginalGraph-->>Partitioner: subgraphs
Partitioner->>Partitioner: 检查 graph_2_graph_partition_info_
Partitioner->>MergedGraph: new ComputeGraph
Note right of MergedGraph: 创建合并图
Partitioner->>SubGraphList: 遍历子图
Note right of SubGraphList: 按拓扑序遍历
loop 每个子图
Partitioner->>SubGraphList: GetSubGraph()
SubGraphList-->>Partitioner: sub_graph
Partitioner->>Partitioner: FindOverflowAttr()
Note right of Partitioner: 查找溢出检测属性
Partitioner->>MergedGraph: AddNode(node)
Note right of MergedGraph: 添加子图节点到合并图<br/>跳过 End/PlaceHolder
end
Partitioner->>Partitioner: RemoveNodeAndEdgeBetweenEndPld()
Note right of Partitioner: 移除 End/PlaceHolder
loop 每个 End-PlaceHolder 对
Partitioner->>EndNode: GetInAnchor()
EndNode-->>Partitioner: end_in_anchor
Partitioner->>EndNode: GetFirstPeerAnchor()
EndNode-->>Partitioner: src_anchor
Partitioner->>Partitioner: GraphUtils::RemoveEdge(src_anchor, end_in_anchor)
Note right of Partitioner: 移除 src->end 边
Partitioner->>PlaceHolderNode: GetOutAnchor()
PlaceHolderNode-->>Partitioner: pld_out_anchor
Partitioner->>PlaceHolderNode: GetPeerAnchors()
PlaceHolderNode-->>Partitioner: peer_in_anchors
loop 每个 peer_in_anchor
Partitioner->>Partitioner: GraphUtils::RemoveEdge(pld_out_anchor, peer_in_anchor)
Note right of Partitioner: 移除 pld->peer 边
Partitioner->>Partitioner: GraphUtils::AddEdge(src_anchor, peer_in_anchor)
Note right of Partitioner: 添加 src->peer 边<br/>恢复原边
end
Partitioner->>EndNode: UnlinkAll()
Partitioner->>PlaceHolderNode: UnlinkAll()
end
Partitioner->>EnginePlacer: Run()
Note right of EnginePlacer: 重新分配引擎
Partitioner->>Partitioner: InheritOriginalAttr()
Note right of Partitioner: 继承原图属性
Partitioner-->>MergedGraph: 合并完成
五、关键设计决策
5.1 为什么用 Cluster 作为分区单元?
决策:用 Cluster(一组节点)作为分区的基本单元,而不是单个节点。
替代方案:
- 节点级别分区:每个节点一个子图,调度开销大
- 算子类型分区:按算子类型分区,无法处理引擎差异
- 用户手动分区:用户负担重,破坏可移植性
权衡分析:
- Cluster 可以最大化子图大小,减少调度开销
- Cluster 内部可以做跨算子优化(如算子融合)
- Cluster 的合并逻辑可以动态调整,适应不同场景
- 代价是 Cluster 的合并逻辑复杂,需要处理环检测、拓扑排序等
代码依据:engine_partitioner.h:35-51
5.2 为什么需要 PlaceHolder/End 机制?
决策:异引擎子图之间通过 PlaceHolder 和 End 算子连接。
设计动机:
- 子图之间需要数据传递,但不能直接连接(不同引擎)
- PlaceHolder 表示占位符,表示来自其他子图的输入
- End 表示结束符,表示输出到其他子图
- 合并时自动移除,恢复原边
PlaceHolder 属性:
peerIndex:对应的 End 算子索引parentOpType:源节点类型parentNodeName:源节点名称anchorIndex:锚点索引
End 属性:
peerIndex:对应的 PlaceHolder 算子索引parentOpType:目标节点类型
代码依据:engine_partitioner.cc:586-634
5.3 为什么需要环检测?
决策:合并 Cluster 时,需要检查是否会产生环。
设计动机:
- Cluster 合并可能产生环(如 A->B->C,合并 A 和 C 会产生环)
- 环会导致拓扑排序失败,无法构建子图
HasSecondPath通过 DFS 检查是否存在第二路径
环检测逻辑:
1 | |
代码依据:engine_partitioner.cc:1221-1260
5.4 为什么需要多线程引擎选择?
决策:引擎选择使用多线程(16线程)并行执行。
设计动机:
- 引擎选择需要调用算子注册信息,检查算子支持
- 检查支持是耗时操作,需要调用 FE 算子库
- 多线程可以加速引擎选择,减少编译时间
实现细节:
- 使用 ThreadPool 创建 16 个线程
- 每个线程独立处理节点
- 使用 mutex 保护共享数据(
node_atomic_engine_map_) - 使用
std::future收集线程结果
代码依据:engine_place.cc:130-180
5.5 为什么需要复合引擎?
决策:除了原子引擎,还需要复合引擎。
设计动机:
- 原子引擎:单个算子引擎(如 AiCore、AiVector)
- 复合引擎:多个原子引擎的组合(如 AiCore+AiVector)
- 复合引擎可以优化跨引擎算子,减少子图数量
实现细节:
AssignCompositeEngine:分配复合引擎atomic_2_composite_:原子引擎到复合引擎的映射- 复合引擎分区模式:
kCompositeEnginePartitioning
代码依据:engine_place.cc:182-207
六、模块间协作关系
6.1 协作模式分析
- EnginePlacer:负责为每个节点分配引擎
- DNNEngineManager:管理所有引擎,提供引擎选择接口
- ThreadPool:多线程执行引擎选择,加速编译
- GraphPartitionInfo:记录分区信息,包括 Cluster、子图、End-PlaceHolder 映射
- PlaceHolder/End:连接异引擎子图,保证数据流正确
七、业界对比与设计洞察
7.1 与其他框架的引擎分区对比
| 框架 | 引擎分区方案 | 设计哲学 | 优缺点 |
|---|---|---|---|
| TensorFlow XLA | 按设备分区(CPU/GPU) | 设备隔离 | 优点:简单;缺点:无法处理同设备多引擎 |
| PyTorch TorchScript | 按算子类型分区 | 算子隔离 | 优点:灵活;缺点:无法优化跨算子 |
| ONNX Runtime | 按执行提供者分区 | 提供者隔离 | 优点:跨框架;缺点:无法做跨提供者优化 |
| GE | 按引擎分区 + Cluster 合并 | 引擎隔离 + 优化 | 优点:最大化优化;缺点:分区逻辑复杂 |
7.2 GE 的独特之处
- Cluster 抽象:用 Cluster 作为分区单元,而不是节点
- PlaceHolder/End 机制:异引擎子图自动连接,保证数据流正确
- 环检测:合并 Cluster 时检查环,避免拓扑排序失败
- 多线程引擎选择:加速编译,减少等待时间
- 复合引擎:优化跨引擎算子,减少子图数量
7.3 如果重新设计,可能的改进方向
引入更智能的合并策略:
- 当前合并策略基于拓扑排序和引擎类型,可以引入性能预估模型
- 根据算子执行时间、内存占用等预估子图性能,优化合并策略
支持跨子图优化:
- 当前子图内部可以做优化,但跨子图无法优化
- 可以引入跨子图算子融合,如将 PlaceHolder 前后的算子融合
增强引擎选择性能:
- 当前引擎选择需要调用 FE 算子库,开销大
- 可以引入引擎选择缓存,避免重复检查
支持动态引擎分区:
- 当前分区是静态的,无法适应动态场景
- 可以引入动态分区机制,根据运行时信息调整分区
八、亮点与问题
8.1 亮点
- 自动化程度高:用户无需关心引擎分区细节,系统自动适配
- Cluster 抽象精妙:用 Cluster 作为分区单元,最大化子图大小
- PlaceHolder/End 机制优雅:异引擎子图自动连接,保证数据流正确
- 环检测保证正确性:合并 Cluster 时检查环,避免拓扑排序失败
- 多线程加速编译:引擎选择并行执行,减少编译时间
8.2 问题
- 分区逻辑复杂:多种分区模式(原子、复合、二次),理解难度大
- 环检测开销大:DFS 检查第二路径,大规模图时开销大
- 引擎选择耗时:调用 FE 算子库检查支持,开销大
- PlaceHolder/End 增加复杂度:合并时需要移除,增加复杂度
- 缺少性能预估:合并策略基于拓扑,缺少性能预估模型
九、总结与启发
9.1 核心启发
- Cluster 是分区的基本单元:用 Cluster 而不是节点,最大化子图大小
- PlaceHolder/End 保证数据流:异引擎子图自动连接,避免数据流断裂
- 环检测保证正确性:合并 Cluster 时检查环,避免拓扑排序失败
- 多线程加速编译:引擎选择并行执行,减少编译时间
- 复合引擎优化跨引擎算子:减少子图数量,提升性能
9.2 适用场景
- 需要处理多引擎算子的框架
- 需要优化流分配的系统
- 需要减少调度开销的场景
十、调用入口汇总
| 调用位置 | 文件路径 | 调用场景 |
|---|---|---|
| 图预处理 | compiler/graph/preprocess/graph_prepare.cc |
图编译前的预处理阶段 |
| 图管理器 | compiler/graph/manager/graph_manager.cc |
图管理器统一入口 |
| FE 图优化 | compiler/engines/nn_engine/optimizer/graph_optimizer/fe_graph_optimizer.cc |
FE 图优化器 |
十一、分区模式详解
11.1 四种分区模式
1 | |
模式说明:
| 模式 | 说明 | 适用场景 |
|---|---|---|
| kAtomicEnginePartitioning | 按原子引擎分区 | 常规分区,每个算子分配原子引擎 |
| kCompositeEnginePartitioning | 按复合引擎分区 | 优化跨引擎算子,减少子图数量 |
| kSecondPartitioning | 二次分区 | 流分配后的二次分区,优化流复用 |
| kMerging | 合并模式 | 子图优化后合并,恢复原图 |
11.2 分区模式的切换流程
stateDiagram-v2
[*] --> AtomicEnginePartitioning
AtomicEnginePartitioning --> CompositeEnginePartitioning: 需要复合引擎优化
AtomicEnginePartitioning --> SecondPartitioning: 流分配后二次分区
CompositeEnginePartitioning --> SecondPartitioning: 流分配后二次分区
SecondPartitioning --> Merging: 子图优化完成
Merging --> AtomicEnginePartitioning: 合并完成,重新分区
Merging --> [*]: 最终合并完成
note right of AtomicEnginePartitioning: 按原子引擎分区<br/>每个算子一个引擎
note right of CompositeEnginePartitioning: 按复合引擎分区<br/>优化跨引擎算子
note right of SecondPartitioning: 二次分区<br/>优化流复用
note right of Merging: 合并子图<br/>恢复原图
十二、引擎选择详解
12.1 引擎选择的优先级
flowchart TD
Start([开始选择引擎]) --> CheckAttr{检查属性}
CheckAttr -->|ATTR_ENGINE_NAME_FOR_LX<br/>已配置| UseAttr[使用属性指定的引擎]
CheckAttr -->|无属性| CheckOpDesc{检查 OpDesc}
CheckOpDesc -->|GetOpEngineName<br/>已有引擎| UseOpDesc[使用 OpDesc 的引擎]
CheckOpDesc -->|无引擎| CallManager[调用 DNNEngineManager]
CallManager --> GetOpInfos[GetOpInfos<br/>获取算子信息]
GetOpInfos --> CheckHostCpu{检查 Host CPU}
CheckHostCpu -->|Host CPU 算子| UseHostCpu[使用 Host CPU 引擎]
CheckHostCpu -->|非 Host CPU| CheckExclude{检查排除引擎}
CheckExclude --> GetExcludeEngines[GetExcludeEngines<br/>获取排除的引擎]
GetExcludeEngines --> CheckSupport[检查算子支持]
CheckSupport -->|不支持| ReturnEmpty[返回空字符串]
CheckSupport -->|支持| SelectBest[选择最佳引擎]
SelectBest --> RecordEngine[记录引擎映射]
UseAttr --> RecordEngine
UseOpDesc --> RecordEngine
UseHostCpu --> RecordEngine
RecordEngine --> End([引擎选择完成])
ReturnEmpty --> End
style Start fill:#e1f5e1
style End fill:#ffe1e1
style CallManager fill:#e1f0ff
style CheckSupport fill:#fff4e1
12.2 引擎选择的实现细节
关键函数:DNNEngineManager::GetDNNEngineName
实现逻辑:
- 获取算子的所有 OpInfo(算子注册信息)
- 检查算子是否为 Host CPU 算子
- 排除不支持的引擎
- 选择最佳引擎(基于性能、支持度等)
代码依据:dnnengine_manager.cc
分析日期:2026-05-08
分析工具:repo-analyzer skill
代码版本:GE trunk_ai/ge