ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

CANN/ge ES模块所有权反转分析

CANN/ge ES模块所有权反转分析 ES Module Ownership Relationship Reversal Analysis【免费下载链接】geGEGraph Engine是面向昇腾的图编译器和执行器提供了计算图优化、多流并行、内存复用和模型下沉等技术手段加速模型执行效率减少模型内存占用。 GE 提供对 PyTorch、TensorFlow 前端的友好接入能力并同时支持 onnx、pb 等主流模型格式的解析与编译。项目地址: https://gitcode.com/cann/geProblem DescriptionThe ownership relationship between Python layer and C layer isreversed:C Layer Ownership Relationshipstruct EsCGraphBuilder { std::liststd::unique_ptrResourceHolder resource_holder_; // Owns all resources std::unique_ptrge::Graph graph_; // ... }; struct EsCTensorHolder { EsCGraphBuilder owner_graph_builder_; // Just a reference, does not own ge::GNode producer_; int32_t producer_out_index_; };Relationship:EsCGraphBuilderownsEsCTensorHolderPython Layer Ownership Relationshipclass GraphBuilder: _handle: EsCGraphBuilderPtr # C object pointer (does not own) class TensorHolder: _handle: EsCTensorHolderPtr # C object pointer (does not own) _builder: GraphBuilder # Strong reference, ownsRelationship:TensorHolderownsGraphBuilderWhy Such Design?Reason: Prevent Dangling Pointersdef create_tensor(): builder GraphBuilder(my_graph) tensor builder.create_const_float(1.0) return tensor # builder will be GC # Problem scenario t create_tensor() # If TensorHolder does not hold GraphBuilder: # - builder has been GC, Python object destroyed # - builder.__del__() calls EsDestroyGraphBuilder() # - underlying C EsCGraphBuilder is destructed # - underlying C EsCTensorHolder is also released (because owned by GraphBuilder) # - t._handle is now a dangling pointer! # Current design (TensorHolder holds GraphBuilder): # - builder Python object is GC, but because t._builder holds reference, wont actually destruct # - underlying C objects remain valid # - t._handle still validPotential Problem AnalysisProblem 1: Circular Reference RiskScenario Descriptionclass GraphBuilder: def __init__(self): self._tensors [] # If saved tensor list def create_const_float(self, value): tensor TensorHolder._create_from(handle, self) self._tensors.append(tensor) # ⚠️ Circular reference! return tensor # Circular reference: # GraphBuilder._tensors - TensorHolder # TensorHolder._builder - GraphBuilderImpactPython GC cannot automatically reclaim (needs to wait for cycle detection)May cause memory leakObject destruction delaySolution# Solution 1: Do not save TensorHolder in GraphBuilder class GraphBuilder: # ❌ Dont do this # self._tensors [] pass # Solution 2: Use weak reference import weakrefclass GraphBuilder: def __init__(self): self._tensors [] # Save weak references def create_const_float(self, value): tensor TensorHolder._create_from(handle, self) self._tensors.append(weakref.ref(tensor)) # Weak reference return tensorCurrent code status: ✅ Safe, GraphBuilder does not save TensorHolder listProblem 2: Semantic InconsistencyC Layer Expectation{ EsCGraphBuilder builder(my_graph); auto tensor1 builder.CreateConstFloat(1.0); auto tensor2 builder.CreateConstFloat(2.0); // tensor1, tensor2 are raw pointers, lifecycle managed by builder } // builder destructs, all tensors also releasedPython Layer Actual Behaviordef test(): builder GraphBuilder(my_graph) tensor1 builder.create_const_float(1.0) return tensor1 t test() # builder out of scope # ✅ tensor1 still valid (because holding builder) # ⚠️ But this is different from C semantics!ImpactAPI semantic confusion: C and Python behavior inconsistentUser confusion: Users familiar with C may misunderstand Python behaviorDocumentation burden: Need extra explanation of differencesIs This Really a Problem?This approach has no problemPython and C lifecycle management are inherently different:Python: Reference counting GCC: RAII manual managementPythonic approach: Objects should remain valid as long as referencedProblem 3: Multi-Builder Scenario LimitationsScenario: Cross-Builder Tensor Usagebuilder1 GraphBuilder(graph1) builder2 GraphBuilder(graph2) tensor1 builder1.create_const_float(1.0) # ❌ Should not be allowed theoretically result builder2_some_op(tensor1) # tensor1 belongs to builder1 # But since tensor1._builder is builder1 # Newly generated tensor will also associate to builder1 # Leading to logical confusionImpactCross-Builder operations may cause underlying graph structure confusionHard to detect and report errorsMay cause C layer assertion failuresSolution# Check builder consistency during operations def add(self, other: TensorHolder) - TensorHolder: if not isinstance(other, TensorHolder): raise TypeError(Operand must be a TensorHolder) # Check if from same builder if self._builder is not other._builder: raise ValueError(Cannot operate on tensors from different GraphBuilders) # ... subsequent logicCurrent code status: Checks have been addedProblem 4: State Management After build_and_reset()Scenario: Continue Using Builder After Buildbuilder GraphBuilder(my_graph) tensor1 builder.create_const_float(1.0) builder.set_graph_output(tensor1, 0) graph builder.build_and_reset() # Build complete # ⚠️ Can we continue using builder? tensor2 builder.create_const_float(2.0) # Not allowed # ⚠️ Can we continue using tensor1? result tensor1 tensor2 # tensor1s builder already in built stateC Layer Implementationstd::unique_ptrge::Graph BuildGraphAndReset() { // ... return std::move(graph_); // Graph object transferred! }Problem: Afterbuild_and_reset(),graph_becomes nullptr, GraphBuilder becomes empty shellImpactBuilders state unclear afterbuild_and_reset()Continued use may cause undefined behaviorOld tensor references builder thats already invalidSolutionclass GraphBuilder: def __init__(self): self._is_built False def build_and_reset(self): if self._is_built: raise RuntimeError(GraphBuilder has already been built) graph_ptr esb_lib.EsBuildGraphAndReset(self._handle) self._is_built True # Mark as built return Graph._create_from(graph_ptr) def create_const_float(self, value): if self._is_built: raise RuntimeError(Cannot create tensors after graph has been built) # ...Current code status: Checks have been addedProblem 5: Graph Object Ownership Management ConflictScenario DescriptionPythonsGraphobject and Csge::Graph*haveopposite ownership semanticsin different usage scenarios, causing resource management conflicts.Ownership Contradiction in Two Usage ScenariosScenario 1: GraphBuilder.build_and_reset() Returnbuilder GraphBuilder(my_graph) x builder.create_input(0) builder.set_graph_output(x, 0) graph builder.build_and_reset() # At this point: Pythons graph object owns C Graph* ownershipC side implementation:EsCGraph *EsBuildGraphAndReset(EsCGraphBuilder *builder) { return static_castEsCGraph *( static_castvoid *(builder-BuildGraphAndReset().release()) // release() transfers ownership ); }Ownership: Python owns, Python responsible for release ✅Scenario 2: Graph Passed as Subgraph Parametersub_graph create_subgraph() # Python owns ownership main_builder GraphBuilder() result If(condition..., then_graphsub_graph, ...) # Problem: sub_graphs C resource taken over by C sideC side implementation:Esphony_IfOutput Esphony_If(..., EsCGraph *then_branch, ...) { auto builder ...-GetOwnerBuilder(); // AddResource takes ownership of then_branch auto then_ptr builder.AddResource( std::unique_ptrge::Graph(then_branch) // C takes ownership ); // ... }Ownership: C owns, C responsible for releaseConflict: If Python also tries to release → Double free!Specific ProblemsProblem 5.1: Double Freebranch_graph create_subgraph() result If(..., then_graphbranch_graph, ...) # Problem: # 1. C side took ownership of branch_graph via AddResource # 2. Pythons branch_graph.__del__() will also call DestroyGraph() # → Double free!Problem 5.2: Subgraph Python Object Becomes Dangling Referencesub_graph create_subgraph() main_builder GraphBuilder() result If(..., then_graphsub_graph, ...) # sub_graph ownership transferred final_graph main_builder.build_and_reset() del main_builder # Explicit del or out of scope, main_builder gets GC # sub_graph._handle points to freed memory print(sub_graph.name) # Accessing wild pointer!Problem Root CauseScenario-Dependent Ownership Semantics:ScenarioShould Python release?Should C release?Expected behaviorbuild_and_reset() return✅ Yes❌ NoPython owns alonePassed as subgraph❌ No✅ YesC owns aloneButGraphclasss__del__()cannot distinguish these two scenarios!class Graph: def __del__(self): # ❌ Problem: Doesnt know which scenario # Scenario 1: Should release # Scenario 2: Should not release destroy_graph(self._handle) # May cause double free!SolutionIntroduce Ownership Marking Mechanismclass Graph: def __init__(self, namegraph): self._handle create_graph(...) self._owns_handle True # ✅ Ownership mark self._owner None # ✅ Ownership taker reference def __del__(self): # ✅ Decide whether to release based on ownership mark if self._owns_handle: destroy_graph(self._handle) def _transfer_ownership_when_pass_as_subgraph(self, new_owner: GraphBuilder): Transfer ownership to C side Args: new_owner: GraphBuilder that takes ownership, keeps reference to prevent it being GC prematurely self._owns_handle False # Python no longer releases self._owner new_owner # Keep reference, prevent new_owner GCAutomated Processing: Code Generator Inserts Ownership Transfer// py_generator_utils.h - GenSubgraphConversion() static void GenSubgraphConversion(...) { // Generate: subgraph._transfer_ownership_when_pass_as_subgraph(owner_graph_builder) ss subgraph_name ._transfer_ownership_when_pass_as_subgraph( owner_graph_builder )\n; }Generated Python code:def If(..., then_graph, else_graph, ...): owner_graph_builder ... # ✅ Auto-generated: Transfer ownership then_graph._transfer_ownership_when_pass_as_subgraph(owner_graph_builder) else_graph._transfer_ownership_when_pass_as_subgraph(owner_graph_builder) result c_lib.EsphonyIf(...) return resultProblem Resolution EffectProblem 5.1 - Double Free: ✅ Resolved_transfer_ownership_when_pass_as_subgraph()sets_owns_handleFalsePythons__del__()no longer releases resourcesOnly C side releasesProblem 5.2 - Subgraph Dangling Reference: ✅ Resolvedsub_graph._ownerholds reference tomain_builderAs long assub_graphexists,main_builderwont be GCAs long asmain_builderexists, C resources remain validReference Chain Protection Mechanismsub_graph create_subgraph() result If(..., then_graphsub_graph, ...)Python Reference Chain: sub_graph (Graph) │ └─ _owner ──────────┐ ↓ result (TensorHolder) main_builder (GraphBuilder) │ │ └─ _builder ────────────┘ └─ _handle → EsCGraphBuilder └─ resource_holder_ └─ [sub_graphs C Graph*] Guarantees: 1. result exists → main_builder exists → C resources valid 2. sub_graph exists → main_builder exists → C resources validCurrent code status: ✅ Implemented and tested【免费下载链接】geGEGraph Engine是面向昇腾的图编译器和执行器提供了计算图优化、多流并行、内存复用和模型下沉等技术手段加速模型执行效率减少模型内存占用。 GE 提供对 PyTorch、TensorFlow 前端的友好接入能力并同时支持 onnx、pb 等主流模型格式的解析与编译。项目地址: https://gitcode.com/cann/ge创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表