Architecture diff: recurse into subgraphs (make control-flow/phase-split changes visible) - #330
Architecture diff: recurse into subgraphs (make control-flow/phase-split changes visible)#330titaiwangms wants to merge 1 commit into
Conversation
…changes are visible The Architecture-Diff tooling collapsed GRAPH-typed attributes (If then_branch / else_branch, Loop / Scan bodies) to a bare type string and never recursed into them. As a result the per-layer static-cache phase-split introduced by PR #328 -- an If(Greater(seq_len, 1)) selecting a prefill (masked) vs decode (Flash) attention path -- was completely invisible to the Architecture Diff CI: the top-level op sequence is unchanged (the If node is present on both sides), so the only signal lives inside the branch subgraphs that were being discarded. This recurses GRAPH and GRAPHS attributes into nested canonical forms so subgraph node structure participates in the comparison, reusing canonicalize_graph (inner node/value names are ignored the same way top-level ones are). diff_graphs gains a dedicated subgraph_structure_change record (MODERATE severity) for structurally significant subgraph deltas -- a node/branch added, removed, rewired, or a subgraph interface change -- while a pure inner-attribute tweak stays changed_attrs (MINOR). Structural significance propagates upward through nested subgraphs (e.g. an If inside an If). The nested diff detail is surfaced in the report (e.g. "then_branch: node[0] Concat: axis: 0 -> 1"). Additive and backward-compatible: non-GRAPH attributes are unchanged and the arch_diff.py consumer (which reads only op_sequence / node counts / the changes list) is unaffected. Adds 16 regression tests covering subgraph recursion, the structural-vs-minor severity boundary (incl. nested), GRAPHS-plural, op-swap no-double-count, and the readable fallback path. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Performance Comparison
|
|
The author of this PR, titaiwangms, is not an activated member of this organization on Codecov. |
|
Thanks - could you run it on #328 to see what it produces? |
…under Developer Tooling Adds an Internal / Developer Tooling subsection so the arch-diff recursion change folded in from #330 is not orphaned in an export-correctness PR. The tool now recurses into If/Loop/Scan subgraphs (with a subgraph_structure_change MODERATE severity) and is landed here because this PR introduces the first control-flow/subgraph change (the static-cache per-layer phase-split) the old top-level-only diff could not see into. Developer-tooling only; no exported-graph or runtime impact. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Review synthesis (4-reviewer fan-out: readability, code, critical, deep + empirical verification)Overall: Clean, well-tested, well-motivated change — recursion is deterministic and name-independent as claimed, idempotence ( Major1. Captured (implicit) outer-value connectivity is severed at the subgraph boundary — a branch rewired to read a different outer tensor is invisible.
2. Minor
Resolved / confirmed fine
NitsCommitted test docstrings carry PR-review artifact IDs ( PraiseStructural significance propagates transitively through 🤖 Generated by a multi-agent review fan-out (readability + code + critical + deep reviewers), with the implicit-capture finding empirically reproduced against the PR branch. |
Addendum — adversarial review (3 additional findings, two empirically reproduced against the PR branch)A fourth reviewer pass surfaced findings beyond the synthesis above. Findings #1 (captured-value connectivity) and #4 (subgraph interface MAJOR→MODERATE downgrade) reinforce the two Majors already posted. Three are new and material: Major —
|
justinchuby
left a comment
There was a problem hiding this comment.
Feel free to merge when you think it is ready, thanks
|
ORT does not support control flow graph without graph break. |
|
Closing this PR for now. The arch-diff subgraph-recursion tooling here was motivated by making the The subgraph-recursion tooling itself is general-purpose and isn't lost — it can |
Motivation
Per the architecture review, the Architecture-Diff tooling should descend
into ONNX subgraphs so control-flow changes are visible in CI. This is the
separate, standalone PR for that work.
The Architecture Diff currently collapses every
GRAPH-typed attribute(an
If'sthen_branch/else_branch, aLoop/Scanbody) to a baretype string and never recurses into it. Any change that lives inside a
subgraph is therefore invisible to the diff, even though the top-level op
sequence is unchanged.
Why it matters now
PR #328 (static-cache attention) introduces a per-layer phase split:
If(Greater(seq_len, 1))selects a prefill (masked) path vs a decode path.Because the
Ifnode itself is present on both the base and head graphs,the top-level op sequence is identical — the only signal that anything
changed lives inside the branch subgraphs that the diff was discarding. So
a structural change of this kind would pass the Architecture Diff CI
silently.
The fix
GRAPHandGRAPHSattributes into nested canonical forms sosubgraph node structure participates in the comparison. This reuses the
existing canonicalization (inner node/value names are ignored the same
way top-level names already are), so it is deterministic and
name-independent.
subgraph_structure_changerecord at MODERATEseverity for structurally significant subgraph deltas — a node or branch
added, removed, or rewired, or a subgraph interface change. A pure
inner-attribute tweak (e.g. a
Concataxis) stayschanged_attrs(MINOR). Structural significance propagates upward through nested
subgraphs (an
Ifinside anIf).(e.g.
then_branch: node[0] Concat: axis: 0 -> 1).The change is additive and backward-compatible: non-
GRAPHattributes arehandled exactly as before, and the existing consumer (which reads only the
op sequence, node counts, and the change list) is unaffected.
Tests
Adds regression coverage for subgraph recursion, the
structural-vs-minor severity boundary (including nested cases),
GRAPHS-plural bodies, op-swap-not-double-counted, and the readablefallback path. Full suite passes; lint and format are clean.
Review
Triple-reviewed (correctness/adversarial, readability, and code review)
prior to opening.
Known limitation / possible follow-up
diff_graphsdoes not compare nodedomainornum_outputs. As a result,a subgraph delta that differs only by operator domain (e.g. a
standard-domain op swapped for a same-named contrib-domain op) is detected
but rated as a non-structural
changed_attrs(MINOR) via the readablesubgraph changedfallback, rather than as a structural change. This ispre-existing behavior at the top level too and is out of scope here; it can
be addressed in a follow-up if desired.