Skip to content

Fix non-deterministic constant folding output - #29789

Merged
tianleiwu merged 2 commits into
microsoft:mainfrom
ducviet00:main
Jul 22, 2026
Merged

Fix non-deterministic constant folding output#29789
tianleiwu merged 2 commits into
microsoft:mainfrom
ducviet00:main

Conversation

@ducviet00

@ducviet00 ducviet00 commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

Description

From v1.24.1, use_tensor_buffer=true is set in TensorToTensorProto during constant folding. For ops that return aliased OrtValues (e.g. Reshape), the folded initializer referenced memory owned by an input initializer. When that input was removed later in the pass, the reference dangled, producing unstable (often huge/negative) values.

Use out_tensor.OwnsBuffer() to decide use_tensor_buffer. Owned tensors are referenced zero-copy (safe via OrtValue shared lifetime); aliased tensors are deep-copied into the proto

Related issue

#29788

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
There may be pipelines that require an authorized user to comment /azp run to run.

@ducviet00

Copy link
Copy Markdown
Contributor Author

After fix:

Iteration #0
2026-07-20 18:17:47.953795884 [W:onnxruntime:, graph.cc:124 MergeShapeInfo] Error merging shape info for output. '/encoder/Concat_3_output_0' source:{4} target:{5}. Falling back to lenient merge.
Op Name: output | Shape: (1, 1, 1024, 1024) | Mean: -11.921855926513672
Op Name: output_1 | Shape: (1, 1, 1024, 1024) | Mean: -14.028903007507324
Op Name: output_2 | Shape: (1, 1, 1024, 1024) | Mean: -13.32435417175293
Iteration #1
2026-07-20 18:18:15.027467821 [W:onnxruntime:, graph.cc:124 MergeShapeInfo] Error merging shape info for output. '/encoder/Concat_3_output_0' source:{4} target:{5}. Falling back to lenient merge.
Op Name: output | Shape: (1, 1, 1024, 1024) | Mean: -11.921855926513672
Op Name: output_1 | Shape: (1, 1, 1024, 1024) | Mean: -14.028903007507324
Op Name: output_2 | Shape: (1, 1, 1024, 1024) | Mean: -13.32435417175293
Iteration #2
2026-07-20 18:18:31.005674489 [W:onnxruntime:, graph.cc:124 MergeShapeInfo] Error merging shape info for output. '/encoder/Concat_3_output_0' source:{4} target:{5}. Falling back to lenient merge.
Op Name: output | Shape: (1, 1, 1024, 1024) | Mean: -11.921855926513672
Op Name: output_1 | Shape: (1, 1, 1024, 1024) | Mean: -14.028903007507324
Op Name: output_2 | Shape: (1, 1, 1024, 1024) | Mean: -13.32435417175293
Iteration #3
2026-07-20 18:18:44.664049009 [W:onnxruntime:, graph.cc:124 MergeShapeInfo] Error merging shape info for output. '/encoder/Concat_3_output_0' source:{4} target:{5}. Falling back to lenient merge.
Op Name: output | Shape: (1, 1, 1024, 1024) | Mean: -11.921855926513672
Op Name: output_1 | Shape: (1, 1, 1024, 1024) | Mean: -14.028903007507324
Op Name: output_2 | Shape: (1, 1, 1024, 1024) | Mean: -13.32435417175293
Iteration #4
2026-07-20 18:18:59.001280850 [W:onnxruntime:, graph.cc:124 MergeShapeInfo] Error merging shape info for output. '/encoder/Concat_3_output_0' source:{4} target:{5}. Falling back to lenient merge.
Op Name: output | Shape: (1, 1, 1024, 1024) | Mean: -11.921855926513672
Op Name: output_1 | Shape: (1, 1, 1024, 1024) | Mean: -14.028903007507324
Op Name: output_2 | Shape: (1, 1, 1024, 1024) | Mean: -13.32435417175293
Iteration #5
2026-07-20 18:19:14.350048792 [W:onnxruntime:, graph.cc:124 MergeShapeInfo] Error merging shape info for output. '/encoder/Concat_3_output_0' source:{4} target:{5}. Falling back to lenient merge.
Op Name: output | Shape: (1, 1, 1024, 1024) | Mean: -11.921855926513672
Op Name: output_1 | Shape: (1, 1, 1024, 1024) | Mean: -14.028903007507324
Op Name: output_2 | Shape: (1, 1, 1024, 1024) | Mean: -13.32435417175293
Iteration #6
2026-07-20 18:19:28.112508013 [W:onnxruntime:, graph.cc:124 MergeShapeInfo] Error merging shape info for output. '/encoder/Concat_3_output_0' source:{4} target:{5}. Falling back to lenient merge.
Op Name: output | Shape: (1, 1, 1024, 1024) | Mean: -11.921855926513672
Op Name: output_1 | Shape: (1, 1, 1024, 1024) | Mean: -14.028903007507324
Op Name: output_2 | Shape: (1, 1, 1024, 1024) | Mean: -13.32435417175293
Iteration #7
2026-07-20 18:19:44.010496067 [W:onnxruntime:, graph.cc:124 MergeShapeInfo] Error merging shape info for output. '/encoder/Concat_3_output_0' source:{4} target:{5}. Falling back to lenient merge.
Op Name: output | Shape: (1, 1, 1024, 1024) | Mean: -11.921855926513672
Op Name: output_1 | Shape: (1, 1, 1024, 1024) | Mean: -14.028903007507324
Op Name: output_2 | Shape: (1, 1, 1024, 1024) | Mean: -13.32435417175293
Iteration #8
2026-07-20 18:20:01.017157428 [W:onnxruntime:, graph.cc:124 MergeShapeInfo] Error merging shape info for output. '/encoder/Concat_3_output_0' source:{4} target:{5}. Falling back to lenient merge.
Op Name: output | Shape: (1, 1, 1024, 1024) | Mean: -11.921855926513672
Op Name: output_1 | Shape: (1, 1, 1024, 1024) | Mean: -14.028903007507324
Op Name: output_2 | Shape: (1, 1, 1024, 1024) | Mean: -13.32435417175293
Iteration #9
2026-07-20 18:20:14.750075880 [W:onnxruntime:, graph.cc:124 MergeShapeInfo] Error merging shape info for output. '/encoder/Concat_3_output_0' source:{4} target:{5}. Falling back to lenient merge.
Op Name: output | Shape: (1, 1, 1024, 1024) | Mean: -11.921855926513672
Op Name: output_1 | Shape: (1, 1, 1024, 1024) | Mean: -14.028903007507324
Op Name: output_2 | Shape: (1, 1, 1024, 1024) | Mean: -13.32435417175293

@ducviet00

ducviet00 commented Jul 20, 2026

Copy link
Copy Markdown
Contributor Author

@tianleiwu
Please take a look 🙇

@tianleiwu tianleiwu left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the fix — the root-cause analysis is correct and the change is minimal and well-targeted.

Why this is right: OwnsBuffer() returns buffer_deleter_ != nullptr. Alias-producing ops (Reshape/Unsqueeze/Squeeze/Flatten/Identity) return non-owning tensors that view an input initializer's buffer, so OwnsBuffer()==false and the data is now deep-copied inline instead of being referenced as in-memory external data that dangles once the parent initializer is removed. For owned outputs, the zero-copy fast path is preserved and the stored ort_value keeps the buffer alive. Small tensors (≤127B) were already inlined, so the fix targets exactly the affected case. The updated comment on the HasExternalData branch also now matches the new behavior.

Non-blocking suggestion: consider adding a regression test (see inline comment) so a future revert to unconditional use_tensor_buffer=true is caught automatically. Otherwise this looks good to me.

Comment thread onnxruntime/core/optimizer/constant_folding.cc
@ducviet00

ducviet00 commented Jul 21, 2026

Copy link
Copy Markdown
Contributor Author

This bug actually affects likely every ONNX model because almost every deep learning model uses alias-producing ops like Reshape, Unsqueeze, Squeeze, Flatten, or Identity. SAM2-UNET just exposed it so clearly because its architecture makes numerical errors snowball from the very beginning of the network, amplifying into wild output fluctuations by the end.

For instance, I just tested it with D-FINE and DEIMv2. When ConstantFolding is enabled, the box means and label counts bounce around randomly across iterations:

Iteration #0
Op Name: labels | Shape: (1, 300) | Mean: 0.7733333333333333
Op Name: boxes | Shape: (1, 300, 4) | Mean: 1881.3662109375
Op Name: scores | Shape: (1, 300) | Mean: 0.14376994967460632
Iteration #1
Op Name: labels | Shape: (1, 300) | Mean: 0.77
Op Name: boxes | Shape: (1, 300, 4) | Mean: 1878.6793212890625
Op Name: scores | Shape: (1, 300) | Mean: 0.14379794895648956
Iteration #2
Op Name: labels | Shape: (1, 300) | Mean: 0.7666666666666667
Op Name: boxes | Shape: (1, 300, 4) | Mean: 1888.7933349609375
Op Name: scores | Shape: (1, 300) | Mean: 0.1436394602060318

With the fix, the outputs are stable and deterministic (same as ConstantFolding is disabled and same as version prior to v1.24.0):

Iteration #0
Op Name: labels | Shape: (1, 300) | Mean: 0.79
Op Name: boxes | Shape: (1, 300, 4) | Mean: 1900.6483154296875
Op Name: scores | Shape: (1, 300) | Mean: 0.14372549951076508
Iteration #1
Op Name: labels | Shape: (1, 300) | Mean: 0.79
Op Name: boxes | Shape: (1, 300, 4) | Mean: 1900.6483154296875
Op Name: scores | Shape: (1, 300) | Mean: 0.14372549951076508
Iteration #2
Op Name: labels | Shape: (1, 300) | Mean: 0.79
Op Name: boxes | Shape: (1, 300, 4) | Mean: 1900.6483154296875
Op Name: scores | Shape: (1, 300) | Mean: 0.14372549951076508

@ducviet00
ducviet00 requested a review from tianleiwu July 22, 2026 16:42
@tianleiwu

Copy link
Copy Markdown
Contributor

@ducviet00 thanks for address the issue. I'll add a test case later.

@tianleiwu
tianleiwu merged commit cd2d4a3 into microsoft:main Jul 22, 2026
96 of 99 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants