Format Inference (Infer Format) Feature Analysis
1. Feature Background
1.1 The Nature of the Problem
When deep learning frameworks (such as PyTorch and TensorFlow) construct computational graphs, users focus on computational semantics—tensor dimensions, mathematical meaning of operators, and data dependencies. However, the hardware architecture of Ascend AI processors (Ascend NPU) has specific requirements for data memory layout. For example:
- Conv2D image input is hardware-friendly with NC1HWC0 format (splitting the C axis with 16 alignment)
- MatMul weights are friendly with FRACTAL_NZ format
- Different operators have different support capabilities and performance preferences for formats
Models described by users in common formats like NCHW or NHWC need to be converted to hardware-friendly memory layouts when executed on Ascend devices. This involves two core issues:
- Semantic Understanding: How to correctly restore the format semantics that users express across the entire computational graph?
- Execution Optimization: How to select appropriate execution formats for operators and minimize the overhead of data reordering (TransData)?
1.2 Why Two Sets of Format Fields Are Needed
GE introduces two representation systems: Origin Format and Storage Format (also called Running Format). The fundamental reason is that semantic correctness and execution efficiency require independent modeling:
| Perspective | Field | Responsibility | Source |
|---|---|---|---|
| Origin | origin_format_ |
Expresses the original format semantics when users construct the computational graph | Explicitly specified by frontend framework or user |
| Storage | format_ |
Describes the actual memory layout during execution | Derived during compilation process |
Using only one Format field would cause the following issues:
- Semantic Loss: When FE (Fusion Engine) converts NCHW to NC1HWC0, the original NCHW semantics have nowhere to be saved. Subsequent optimization Passes cannot determine what this tensor originally represented.
- Limited Optimization: Debugging scenarios like Data Dump and Profiling need to convert NC1HWC0 data back to NCHW for user understanding. Without Origin Format, correct restoration is impossible.
- Format Propagation Confusion: Whole-network format inference requires anchor points (such as Conv2D's data_format attribute). If the execution format overwrites the semantic format, the starting point for inference is lost.
Specifically, taking an NCHW tensor [8, 3, 224, 224] as an example:
| Field | Value | Meaning |
|---|---|---|
| OriginFormat | NCHW | User-defined semantic format |
| OriginShape | [8, 3, 224, 224] | Dimensions understood by user |
| StorageFormat | NC1HWC0 | Actual memory layout |
| StorageShape | [8, 1, 224, 224, 16] | Actual storage form after C=3 is aligned up to 16 |
The OriginFormat cannot be uniquely restored from StorageShape [8, 1, 224, 224, 16] alone—it could come from NCHW (C=3) or NHWC, so both fields must coexist.
2. User Scenarios
2.1 Offline Compilation Scenario (atc)
When users use the atc tool to compile ONNX/PB models into OM files, GE automatically completes the whole-network format inference:
- Model inputs are defined in NCHW or NHWC format
- GE infers the OriginFormat for each operator
- FE selects StorageFormat based on operator capabilities and performance preferences
- Finally generates an OM model containing correct memory layout information
2.2 Online Training/Inference Scenario
When integrating through TorchAir or TFA, the computational graph passed by the framework may not explicitly mark all operator formats. During the graph compilation phase, GE needs to:
- Infer OriginFormat for all tensors in the graph
- Refresh Data/NetOutput nodes during the
PrepareRunningFormatRefinerphase based on user-setstorage_formatattribute - Ensure InferShape executes in the correct format context
2.3 Data Dump and Profiling
When users debug, they need to view operator input and output data. Data is stored on the device in StorageFormat, but users understand OriginFormat. GE saves the ATTR_NAME_DATA_DUMP_ORIGIN_FORMAT attribute and converts data from StorageFormat back to OriginFormat during Dump.
3. External Interfaces
3.1 Format Interfaces on GeTensorDesc
GeTensorDesc (defined in inc/graph_metadef/graph/ge_tensor.h) is the core class for tensor description, providing the following format-related interfaces:
GeTensorDesc
├── GetFormat() / SetFormat() // Read/write StorageFormat
├── GetOriginFormat() / SetOriginFormat() // Read/write OriginFormat
├── GetShape() / SetShape() // Read/write StorageShape
├── GetOriginShape() / SetOriginShape() // Read/write OriginShape
In the TensorDescImpl class in graph_metadef/graph/normal_graph/tensor.cc, you can see these two fields are stored independently:
format_: corresponds to StorageFormat, defaults toFORMAT_NDorigin_format_: corresponds to OriginFormat, defaults toFORMAT_NDorigin_format_is_set_: marks whether OriginFormat has been explicitly set
3.2 StorageFormat Descriptor
At runtime (gert namespace), StorageFormat is a descriptor that carries both Origin and Storage information (defined in inc/graph_metadef/external/graph/types.h), constructed as:
StorageFormat(origin_format, storage_format, expand_dims_type)
In the GetTensorHolder function in graph_metadef/register/shape_inference.cc, you can see that when creating a Tensor, the two format fields of GeTensorDesc are mapped separately:
{input_desc.GetOriginFormat(), input_desc.GetFormat(), {}}
That is, the first parameter of the StorageFormat descriptor is OriginFormat, and the second parameter is StorageFormat.
3.3 Operator InferFormat Registration Interface
Operator developers can register format inference functions through:
IMPL_OP(OpType).InferFormat(infer_format_func)
Where infer_format_func has the signature UINT32(InferFormatContext *context).
The V2 interface provides more structured input/output access through InferFormatContext. In the UpdateOpDescOutFormat function in graph_metadef/register/shape_inference.cc, after V2 inference completes, results are written back to OpDesc:
desc->SetOriginFormat(format->GetOriginFormat());
desc->SetFormat(format->GetStorageFormat());
3.4 User-Specified StorageFormat
Users can specify operator StorageFormat by setting attributes on TensorDesc:
AttrUtils::SetInt(tensor_desc, ATTR_NAME_STORAGE_FORMAT, format_value);
AttrUtils::SetListInt(tensor_desc, ATTR_NAME_STORAGE_SHAPE, shape_dims);
These attributes are consumed during the PrepareRunningFormatRefiner phase in compiler/graph/preprocess/graph_prepare.cc to refresh Data node and NetOutput node formats.
3.5 Format Enumeration Definition
Format types supported by GE are defined in inc/framework/executor_c/types.h. Core formats include:
| Format | Description | Typical Use |
|---|---|---|
| FORMAT_ND | N-dimensional tensor | Default format, carries no special semantics |
| FORMAT_NCHW | N-C-H-W | Common convolution format on user side |
| FORMAT_NHWC | N-H-W-C | TensorFlow default format |
| FORMAT_NC1HWC0 | N-C1-H-W-C0 | Conv2D friendly format on Ascend |
| FORMAT_FRACTAL_Z | C1HW-N1-N0-C0 | Conv2D Filter friendly format |
| FORMAT_FRACTAL_NZ | N1-N0-C1-C0 | MatMul weight friendly format |
4. Implementation Details
4.1 Overall Process
The position of format inference in the GE compilation process:
flowchart TD
A[图解析完成] --> B[InferOriginFormat<br>FormatRefiner]
B --> C[InferShape<br>ShapeRefiner]
C --> D[PrepareRunningFormatRefiner<br>GraphPrepare]
D --> E[FE 格式选择与扩散]
E --> F[TransData 插入与优化]
Key sequence: Infer OriginFormat first, then do InferShape, finally handle StorageFormat. This is because InferShape needs to execute in the correct OriginFormat context (Shape dimension meaning depends on Format), while StorageFormat selection occurs during the FE phase.
4.2 Origin Format Inference (FormatRefiner)
The core implementation of Origin Format inference is in the FormatRefiner::InferOrigineFormat function in graph_metadef/graph/refiner/format_refiner.cc.
4.2.1 Inference Algorithm
The inference uses an anchor propagation strategy:
flowchart TD
A[扫描全图节点] --> B{所有输入输出<br>格式都是 ND?}
B -- 是 --> C[加入 anchor_data_nodes<br>待后续处理]
B -- 否 --> D[标记为 anchor_point<br>已携带格式语义]
D --> E[RefreshOriginFormatOfAnchor<br>将 format 刷新到 origin_format]
E --> F[从锚点向前/向后<br>扩散格式推导]
F --> G[DataNodeFormatProcess<br>为未推导的 Data 节点<br>设置全局格式]
Specific steps:
-
Anchor Identification (
GetAnchorPoints): Traverse all nodes in the graph and find nodes with non-ND formats in their inputs/outputs as anchor points. These nodes are typically format-sensitive operators like Conv2D and Pooling, which already carry format information through attributes (such asdata_format). -
Anchor Refresh (
RefreshOriginFormatOfAnchor): For anchor nodes, iforigin_formatis still ND or RESERVED, copy theformatvalue toorigin_format. This ensures the anchor's own OriginFormat is correctly established. -
Bidirectional Propagation (
AnchorProcess):- Backward Inference (
BackInferProcess): Starting from the anchor's input end, propagate format backward along the data flow. For each upstream node, if itsorigin_formatis ND and unlocked, pass the anchor's format to it. - Forward Inference (
ForwardInferProcess): Starting from the anchor's output end, propagate format forward along the data flow.
- Backward Inference (
-
Data Node Fallback (
DataNodeFormatProcess): For Data nodes not touched during inference (usually due to missing format anchors), set the format uniformly using the graph's globaldata_formatparameter.
4.2.2 Interruption Conditions for Format Propagation
The inference process interrupts under the following conditions:
- Node format is locked (
ATTR_NAME_FORMAT_LOCKEDis true): Some operators' formats should not be overwritten by the inference process - Node is a dimension-changing operator (PERMUTE, EXPANDDIMS, SQUEEZE): When the number of dimensions is less than 4, dimension semantics are uncertain and format should not be propagated
- Encountering a scalar (dim_num == 0): Scalars have no format semantics
- Encountering ND format: ND indicates no specific format semantics, propagation stops here
- Encountering NetOutput: As the graph boundary, do not continue forward propagation
4.2.3 Ref Reflection Mechanism
For control flow operators like If/Case, GE establishes reflection relationships between subgraph and main graph Data nodes through RefRelations. When a Data's format is inferred in the main graph, ReflectionProcess synchronizes the format to the corresponding Data node in the subgraph and its parent node's input.
4.2.4 Operator Custom Inference
Besides the default format propagation mechanism, operators can also register custom InferFormat functions. In NodeUtilsEx::InferOriginFormat, OpDescUtilsEx::CallInferFormatFunc is called to preferentially use the operator's registered inference function; otherwise, DefaultInferFormat is used (propagating the first non-ND format to all inputs and outputs).
4.3 Storage Format Determination
Storage Format determination has two paths:
4.3.1 User Explicit Specification Path
In the UpdateDataNetOutputByStorageFormat function in compiler/graph/preprocess/graph_prepare.cc:
- For Data nodes: Read user-specified StorageFormat from
ATTR_NAME_STORAGE_FORMATandATTR_NAME_STORAGE_SHAPEattributes, callModifyTensorDescStorageFormatAndShapeto refresh TensorDesc - For NetOutput nodes: Similarly read attributes and refresh to ensure correct output format
- For ConstPlaceHolder nodes: Handle constant storage format
Core operations of the ModifyTensorDescStorageFormatAndShape function:
- Calculate storage form (StorageShape) based on StorageFormat and OriginFormat, including dimension expansion and alignment
- Call
SetFormatto set StorageFormat (note: notSetOriginFormat) - Call
SetShapeto set StorageShape - Calculate and set Tensor memory size
4.3.2 FE Automatic Selection Path
For compute-intensive operators (such as Conv2D and MatMul), FE (Fusion Engine) selects the optimal StorageFormat during operator compilation based on operator capabilities. This process is implemented by the FormatSelector system under compiler/engines/nn_engine/optimizer/format_selector/:
FormatDtypeOpBuiltinSelector: Format selection for built-in operatorsFormatDtypeOpKernelSelector: Format selection based on operator kernel informationFormatDtypeOpCustomizeSelector: Format selection for custom operators
These Selectors are coordinated by FormatDtypeManagerBase and finally set to graph nodes by FormatDtypeSetter.
4.4 Runtime Format Inference (InferShape Phase)
During runtime InferShape phase, format information is passed to operator inference functions through InferFormatContext (V2 interface).
In the InferFormatOnCompile function in graph_metadef/register/shape_inference.cc:
- Construct
InferFormatContext: CreateCompileTimeTensorDescfor each input/output, containing bothorigin_formatandstorage_format - Call the operator's registered
infer_format_func - Write inference results back to OpDesc through
UpdateOpDescOutFormat
In the GetTensorHolder function, you can see how Tensor is created:
gert::Tensor(storage_shape,
{input_desc.GetOriginFormat(), input_desc.GetFormat(), {}},
input_desc.GetDataType())
Where the StorageShape descriptor carries both Origin and Storage Shape information.
4.5 Format Difference Detection and TransData Insertion
In runtime/v2/graph_builder/storage_format.cc, the DiffStorageFormat function detects whether a tensor's OriginFormat differs from StorageFormat (or whether Shape differs):
return td->GetFormat() != td->GetOriginFormat() ||
td->GetShape().GetDims() != td->GetOriginShape().GetDims()
AnyDiffStorageFormat checks whether any format difference exists among all inputs and outputs of a node. If a difference exists, a TransData operator is inserted during the Lowering phase to perform the actual format conversion.
4.6 Format Refresh in InferShape
In the compatibility InferShape in runtime/v2/kernel/common_kernel_impl/infer_shape_compatible.cc, there is a key format handling:
// RT1时,算子的infershape只能拿到format字段,但是却需要用origin format
input_desc->SetFormat(input_desc_in_context->GetOriginFormat());
input_desc->SetOriginFormat(input_desc_in_context->GetOriginFormat());
This indicates that in RT1 (Runtime version 1) compatibility mode, InferShape can only access the format field, but actually needs OriginFormat. Therefore, OriginFormat needs to be correctly retrieved from Context and synchronously set to both format and origin_format fields.
In the TransformOutputShape function in runtime/v2/kernel/common_kernel_impl/infer_shape.h, when OriginFormat differs from StorageFormat, ShapeTransferAccordingToFormat::TransferShape is called to convert OriginShape to StorageShape:
if (output_td->GetOriginFormat() == output_td->GetStorageFormat()) {
// 格式相同,无需转换
return GRAPH_SUCCESS;
}
// 格式不同,需要根据 StorageFormat 计算 StorageShape
TransferShape(origin_format, storage_format, data_type, storage_shape)
4.7 Invocation Timing in Whole-Network Compilation Process
In compiler/graph/manager/graph_manager.cc, format-related phases execute in the following order:
PrepareRunningFormatRefiner ← StorageFormat 刷新
→ UpdateDataNetOutputByStorageFormat
→ VariablePrepareOpPass
→ UpdateInputOutputByOptions
→ UpdateVariableFormats
Before this, in GraphPrepare::GenerateInfershapeGraph:
InferOriginFormat ← OriginFormat 推导
→ FormatRefiner::InferOrigineFormat
4.8 Format Optimization Pass
After OriginFormat inference and StorageFormat determination, multiple Passes under compiler/graph/passes/format_optimize/ are responsible for optimizing TransData insertion and elimination:
| Pass | Function |
|---|---|
TransOpSymmetryEliminationPass |
Eliminate symmetric format conversion pairs (such as NCHW→NC1HWC0→NCHW) |
TransOpBreadthFusionPass |
Merge multiple TransData on the same node's output side |
TransOpWithoutReshapeFusionPass |
Fuse consecutive TransData without Reshape |
TransposeTransDataPass |
Merge and optimize Transpose with TransData |
UnchangedTransposeRemovePass |
Remove Transpose that does not change data |
CastRemovePass |
Remove unnecessary Cast |
5. Key Design Decisions
5.1 Why FormatRefiner Uses Anchor Propagation Instead of Full Graph Traversal
Advantages of anchor propagation:
- Avoid meaningless propagation: Large numbers of ElementWise operators (such as Add and ReLU) are format-insensitive. Their formats should remain consistent with upstream and do not need separate inference.
- Reduce complexity: Only perform inference near nodes where format semantics change (anchors), rather than traversing the entire graph.
- Support control flow: Anchor propagation combined with RefRelations can naturally handle format synchronization between subgraphs.
5.2 Why OriginFormat and StorageFormat Are Both Stored as format_ and origin_format_ in GeTensorDesc
Core considerations for this design:
origin_format_is determined in the FormatRefiner phase, expresses user semantics, and should not be modified once determinedformat_initially equalsorigin_format_, and is later modified to StorageFormat during the FE phase- Both fields coexisting allows comparison at any time to determine whether TransData is needed
5.3 Why StorageFormat Descriptor Carries Origin Information
Although StorageFormat (gert namespace) is named "Storage", it is actually a composite descriptor. The reason is that the OriginFormat cannot be uniquely restored from the StorageFormat value alone (such as NC1HWC0)—it could be NCHW or NHWC—so both must be saved simultaneously. This allows the runtime to obtain complete format context without backtracking to OpDesc.
6. Data Flow Summary
The following is the complete lifecycle of format information in a typical Conv2D network:
sequenceDiagram
participant Parser as 图解析
participant FR as FormatRefiner
participant SR as ShapeRefiner
participant GP as GraphPrepare
participant FE as 融合引擎
participant RT as 运行时
Parser->>Parser: 解析模型,Data 节点格式=NCHW
Note over Parser: format_=NCHW, origin_format_=ND
FR->>FR: 识别 Conv2D 为锚点
FR->>FR: 设置 Conv2D origin_format_=NCHW
FR->>FR: 向前传播 Data origin_format_=NCHW
FR->>FR: 向后传播 ReLU origin_format_=NCHW
Note over FR: origin_format_ 全部确定
SR->>SR: 基于 OriginFormat 推导 Shape
Note over SR: origin_shape_ 确定
GP->>GP: 消费 ATTR_NAME_STORAGE_FORMAT
GP->>GP: 刷新 Data/NetOutput 的 format_
FE->>FE: 为 Conv2D 选择 NC1HWC0
FE->>FE: 设置 Conv2D format_=NC1HWC0
Note over FE: format_ ≠ origin_format_
FE->>RT: 插入 TransData (NCHW→NC1HWC0)
RT->>RT: 执行时按 StorageFormat 访问内存
RT->>RT: Dump 时按 OriginFormat 还原数据