Skip to content

Add C++ IR (Graph) entry point to the optimizer - #319

Open
take-cheeze wants to merge 1 commit into
onnx:mainfrom
onnxsim:claude/onnx-cpp-ir-optimizer-api-rrjtni
Open

Add C++ IR (Graph) entry point to the optimizer#319
take-cheeze wants to merge 1 commit into
onnx:mainfrom
onnxsim:claude/onnx-cpp-ir-optimizer-api-rrjtni

Conversation

@take-cheeze

Copy link
Copy Markdown
Member

Previously every public entry point required a ModelProto (or serialized bytes / file path); the in-memory ONNX C++ IR (Graph) was only ever built internally via ImportModelProto and torn down via ExportModelProto, never exposed as an input. A C++ caller already holding a Graph had to pay a full serialize/parse round-trip on both sides just to run the passes.

Add in-place Graph entry points that run the configured passes directly on the IR:

  • Optimizer::optimize(Graph&) overload that runs the pass manager on the graph in place; the existing optimize(ModelProto&) now reuses it.
  • Free functions OptimizeGraph / OptimizeGraphFixed mirroring Optimize / OptimizeFixed but operating on a Graph&.

Proto-level concerns (ir_version upgrade, function copying) stay on the ModelProto path since they have no Graph equivalent. The additions are purely additive with no ABI break. Add a gtest covering the Graph path.

Claude-Session: https://claude.ai/code/session_01U7PHiYF9q8Tv1KWV5w65Hq

@take-cheeze
take-cheeze requested review from a team as code owners July 26, 2026 03:07
Previously every public entry point required a ModelProto (or serialized
bytes / file path); the in-memory ONNX C++ IR (Graph) was only ever built
internally via ImportModelProto and torn down via ExportModelProto, never
exposed as an input. A C++ caller already holding a Graph had to pay a full
serialize/parse round-trip on both sides just to run the passes.

Add in-place Graph entry points that run the configured passes directly on
the IR:

- Optimizer::optimize(Graph&) overload that runs the pass manager on the
  graph in place; the existing optimize(ModelProto&) now reuses it.
- Free functions OptimizeGraph / OptimizeGraphFixed mirroring
  Optimize / OptimizeFixed but operating on a Graph&.

Proto-level concerns (ir_version upgrade, function copying) stay on the
ModelProto path since they have no Graph equivalent. The additions are
purely additive with no ABI break. Add a gtest covering the Graph path.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01U7PHiYF9q8Tv1KWV5w65Hq
Signed-off-by: take-cheeze <takechi101010@gmail.com>
@take-cheeze
take-cheeze force-pushed the claude/onnx-cpp-ir-optimizer-api-rrjtni branch from 91bbecc to 880ef9d Compare July 26, 2026 03:09
take-cheeze added a commit to onnxsim/onnxsim that referenced this pull request Aug 18, 2026
pull Bot pushed a commit to naonao-cola/onnx-simplifier that referenced this pull request Aug 18, 2026
Ports onnx/optimizer#319 (Optimizer::optimize(Graph&), OptimizeGraph,
OptimizeGraphFixed) into onnxsim's onnx-optimizer fork: a C++ caller
that already holds a Graph can run the configured passes directly on
it, with no ModelProto <-> Graph round trip at all.

onnxsim's own ModelProto-based Optimize()/OptimizeFixed() calls
(onnxsim.cpp) already round-trip through ModelProto on every round of
OptAndShape's fixed point, alternating with InferShapes; the fixed
point's convergence check (FixedPointFn's bool-returning overload)
stops as soon as *either* side reports no change, so there is no
"several consecutive Optimize-only rounds on a resident Graph" window
in the current algorithm to route through the new Graph-native entry
point for a further round-trip reduction there -- that still needs
shape inference itself ported to Graph IR (onnx issue onnxsim#633's "remaining
option 2", "a different order of magnitude of work" per that issue).

This bump lands the primitive and keeps onnxsim/optimizer's
ModelProto-based optimize() sharing one code path with it (the fork's
two ModelProto overloads now delegate to optimize(Graph&, report)
internally instead of calling pass_manager->run() directly), without
changing onnxsim.cpp's behavior.
take-cheeze pushed a commit to onnxsim/optimizer that referenced this pull request Aug 21, 2026
… through them

Stacked on onnx#324 (per-pass transform-count report on optimize()) -- this
extends that same report plumbing to two more entry points.

- Optimizer::optimize(Graph&, report=nullptr) / free functions
  OptimizeGraph/OptimizeGraphFixed(Graph&, names, report=nullptr): run the
  configured passes directly on an in-memory Graph, with no ModelProto <->
  Graph round trip at all, for a C++ caller that already holds a Graph.
  Ports onnx#319 (Optimizer::optimize(Graph&), OptimizeGraph,
  OptimizeGraphFixed) and extends it with the report out-param onnx#324 added
  to the ModelProto-based entry points, so a Graph-native caller gets the
  same "did this pass change anything" signal. Not gated behind
  ONNX_IR_PB_CONVERTER_HAS_CONSUMING_OVERLOADS, since these never touch
  ModelProto at all. The two existing ModelProto-based optimize()
  overloads now delegate to this internally instead of calling
  pass_manager->run() directly, so there is one shared code path.
- A consuming (moving) Optimizer::optimize(ModelProto&, ...) overload
  (also gated behind ONNX_IR_PB_CONVERTER_HAS_CONSUMING_OVERLOADS), for a
  caller about to discard or overwrite its input model, avoiding a copy.
- Extend the CSE/duplicate-initializer BLAKE3-digest caching (introduced
  in the companion "trust a BLAKE3 content digest" PR) with a
  clear_tensor_digest_cache option on the Graph-native optimize() overload
  and OptimizeGraph/OptimizeGraphFixed, plus a
  ClearTensorContentDigestCache() the caller can invoke explicitly:
  a caller that reuses the same resident Graph across several optimize()
  calls (e.g. a fixed-point loop that alternates optimize() with shape
  inference) can now keep tensor digests cached across those calls instead
  of paying to recompute them every round, while every other caller keeps
  the safe default of clearing the cache on each call.
- Add pass-phase profiling instrumentation (ONNXOPTIMIZER_PROFILE_PASS_PHASES
  env var) for PredicateBasedPass's matching vs. modifying phases, and use
  Value::hasUsesInCurrentGraph() (the companion onnx/onnx PR) instead of
  uses().size() in eliminate_deadend/eliminate_common_subexpression's
  hot-path use-count checks, avoiding an O(N) scan of each value's uses
  just to test for zero/one.

Depends on onnx#324 (this branch's parent) and on the companion onnx/onnx PR
for Value::hasUsesInCurrentGraph(). onnx#319 is still open upstream; this
supersedes it by including its content plus the report extension, so it
can be closed as included in this PR if maintainers prefer that ordering,
or this can be rebased onto onnx#319 once it merges instead.

Signed-off-by: take-cheeze <takechi101010@gmail.com>
take-cheeze pushed a commit to onnxsim/optimizer that referenced this pull request Aug 21, 2026
… through them

Stacked on onnx#324 (per-pass
transform-count report on optimize()) -- this extends that same report
plumbing to two more entry points.

- Optimizer::optimize(Graph&, report=nullptr) / free functions
  OptimizeGraph/OptimizeGraphFixed(Graph&, names, report=nullptr): run the
  configured passes directly on an in-memory Graph, with no ModelProto <->
  Graph round trip at all, for a C++ caller that already holds a Graph.
  Ports onnx#319
  (Optimizer::optimize(Graph&), OptimizeGraph, OptimizeGraphFixed) and
  extends it with the report out-param
  onnx#324 added to the ModelProto-based
  entry points, so a Graph-native caller gets the same "did this pass
  change anything" signal. Not gated behind
  ONNX_IR_PB_CONVERTER_HAS_CONSUMING_OVERLOADS, since these never touch
  ModelProto at all. The two existing ModelProto-based optimize()
  overloads now delegate to this internally instead of calling
  pass_manager->run() directly, so there is one shared code path.
- A consuming (moving) Optimizer::optimize(ModelProto&, ...) overload
  (also gated behind ONNX_IR_PB_CONVERTER_HAS_CONSUMING_OVERLOADS), for a
  caller about to discard or overwrite its input model, avoiding a copy.
- Extend the CSE/duplicate-initializer BLAKE3-digest caching (introduced
  in the companion "trust a BLAKE3 content digest" PR,
  onnx/optimizer@main...onnxsim:optimizer:claude/upstream-cse-hashing-overhaul,
  not yet opened as its own PR as of this writing) with a
  clear_tensor_digest_cache option on the Graph-native optimize() overload
  and OptimizeGraph/OptimizeGraphFixed, plus a
  ClearTensorContentDigestCache() the caller can invoke explicitly:
  a caller that reuses the same resident Graph across several optimize()
  calls (e.g. a fixed-point loop that alternates optimize() with shape
  inference) can now keep tensor digests cached across those calls instead
  of paying to recompute them every round, while every other caller keeps
  the safe default of clearing the cache on each call.
- Add pass-phase profiling instrumentation (ONNXOPTIMIZER_PROFILE_PASS_PHASES
  env var) for PredicateBasedPass's matching vs. modifying phases, and use
  Value::hasUsesInCurrentGraph() (the companion onnx/onnx PR,
  onnx/onnx#8346) instead of uses().size() in
  eliminate_deadend/eliminate_common_subexpression's hot-path use-count
  checks, avoiding an O(N) scan of each value's uses just to test for
  zero/one.

Depends on onnx#324 (this branch's
parent) and on onnx/onnx#8346 for
Value::hasUsesInCurrentGraph(). onnx#319
is still open upstream; this supersedes it by including its content plus
the report extension, so it can be closed as included in this PR if
maintainers prefer that ordering, or this can be rebased onto onnx#319 once
it merges instead.

Signed-off-by: take-cheeze <takechi101010@gmail.com>
take-cheeze pushed a commit to onnxsim/optimizer that referenced this pull request Aug 21, 2026
… through them

Stacked on onnx#324 (per-pass
transform-count report on optimize()) -- this extends that same report
plumbing to two more entry points.

- Optimizer::optimize(Graph&, report=nullptr) / free functions
  OptimizeGraph/OptimizeGraphFixed(Graph&, names, report=nullptr): run the
  configured passes directly on an in-memory Graph, with no ModelProto <->
  Graph round trip at all, for a C++ caller that already holds a Graph.
  Ports onnx#319
  (Optimizer::optimize(Graph&), OptimizeGraph, OptimizeGraphFixed) and
  extends it with the report out-param
  onnx#324 added to the ModelProto-based
  entry points, so a Graph-native caller gets the same "did this pass
  change anything" signal. Not gated behind
  ONNX_IR_PB_CONVERTER_HAS_CONSUMING_OVERLOADS, since these never touch
  ModelProto at all. The two existing ModelProto-based optimize()
  overloads now delegate to this internally instead of calling
  pass_manager->run() directly, so there is one shared code path.
- A consuming (moving) Optimizer::optimize(ModelProto&, ...) overload
  (also gated behind ONNX_IR_PB_CONVERTER_HAS_CONSUMING_OVERLOADS), for a
  caller about to discard or overwrite its input model, avoiding a copy.
- Extend the CSE/duplicate-initializer BLAKE3-digest caching (introduced
  in the companion "trust a BLAKE3 content digest" PR,
  onnx/optimizer@main...onnxsim:optimizer:claude/upstream-cse-hashing-overhaul,
  not yet opened as its own PR as of this writing) with a
  clear_tensor_digest_cache option on the Graph-native optimize() overload
  and OptimizeGraph/OptimizeGraphFixed, plus a
  ClearTensorContentDigestCache() the caller can invoke explicitly:
  a caller that reuses the same resident Graph across several optimize()
  calls (e.g. a fixed-point loop that alternates optimize() with shape
  inference) can now keep tensor digests cached across those calls instead
  of paying to recompute them every round, while every other caller keeps
  the safe default of clearing the cache on each call.
- Add pass-phase profiling instrumentation (ONNXOPTIMIZER_PROFILE_PASS_PHASES
  env var) for PredicateBasedPass's matching vs. modifying phases, and use
  Value::hasUsesInCurrentGraph() (the companion onnx/onnx PR,
  onnx/onnx#8346) instead of uses().size() in
  eliminate_deadend/eliminate_common_subexpression's hot-path use-count
  checks, avoiding an O(N) scan of each value's uses just to test for
  zero/one.

Depends on onnx#324 (this branch's
parent) and on onnx/onnx#8346 for
Value::hasUsesInCurrentGraph(). onnx#319
is still open upstream; this supersedes it by including its content plus
the report extension, so it can be closed as included in this PR if
maintainers prefer that ordering, or this can be rebased onto onnx#319 once
it merges instead.

Signed-off-by: take-cheeze <takechi101010@gmail.com>
take-cheeze pushed a commit to onnxsim/optimizer that referenced this pull request Aug 21, 2026
… through them

Stacked on onnx#324 (per-pass
transform-count report on optimize()) -- this extends that same report
plumbing to two more entry points.

- Optimizer::optimize(Graph&, report=nullptr) / free functions
  OptimizeGraph/OptimizeGraphFixed(Graph&, names, report=nullptr): run the
  configured passes directly on an in-memory Graph, with no ModelProto <->
  Graph round trip at all, for a C++ caller that already holds a Graph.
  Ports onnx#319
  (Optimizer::optimize(Graph&), OptimizeGraph, OptimizeGraphFixed) and
  extends it with the report out-param
  onnx#324 added to the ModelProto-based
  entry points, so a Graph-native caller gets the same "did this pass
  change anything" signal. Not gated behind
  ONNX_IR_PB_CONVERTER_HAS_CONSUMING_OVERLOADS, since these never touch
  ModelProto at all. The two existing ModelProto-based optimize()
  overloads now delegate to this internally instead of calling
  pass_manager->run() directly, so there is one shared code path.
- A consuming (moving) Optimizer::optimize(ModelProto&, ...) overload
  (also gated behind ONNX_IR_PB_CONVERTER_HAS_CONSUMING_OVERLOADS), for a
  caller about to discard or overwrite its input model, avoiding a copy.
- Extend the CSE/duplicate-initializer BLAKE3-digest caching (introduced
  in the companion "trust a BLAKE3 content digest" PR,
  onnx/optimizer@main...onnxsim:optimizer:claude/upstream-cse-hashing-overhaul,
  not yet opened as its own PR as of this writing) with a
  clear_tensor_digest_cache option on the Graph-native optimize() overload
  and OptimizeGraph/OptimizeGraphFixed, plus a
  ClearTensorContentDigestCache() the caller can invoke explicitly:
  a caller that reuses the same resident Graph across several optimize()
  calls (e.g. a fixed-point loop that alternates optimize() with shape
  inference) can now keep tensor digests cached across those calls instead
  of paying to recompute them every round, while every other caller keeps
  the safe default of clearing the cache on each call.
- Add pass-phase profiling instrumentation (ONNXOPTIMIZER_PROFILE_PASS_PHASES
  env var) for PredicateBasedPass's matching vs. modifying phases, and use
  Value::hasUsesInCurrentGraph() (the companion onnx/onnx PR,
  onnx/onnx#8346) instead of uses().size() in
  eliminate_deadend/eliminate_common_subexpression's hot-path use-count
  checks, avoiding an O(N) scan of each value's uses just to test for
  zero/one.

Note: Graph::initializers_ stays a plain vector<Tensor> throughout (see
the companion onnx/onnx branch above) -- an earlier revision here also
adapted this pass's initializer loop to a vector<unique_ptr<Tensor>>
storage change, since reverted after a benchmark showed no benefit; see
the parent commit's own log for that measurement.

Depends on onnx#324 (this branch's
parent) and on onnx/onnx#8346 for
Value::hasUsesInCurrentGraph(). onnx#319
is still open upstream; this supersedes it by including its content plus
the report extension, so it can be closed as included in this PR if
maintainers prefer that ordering, or this can be rebased onto onnx#319 once
it merges instead.

Signed-off-by: take-cheeze <takechi101010@gmail.com>
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