Skip to content

Remove permutes around fused_quant elementwise ops - #21481

Open
mcremon-meta wants to merge 1 commit into
mainfrom
export-D113424191
Open

Remove permutes around fused_quant elementwise ops#21481
mcremon-meta wants to merge 1 commit into
mainfrom
export-D113424191

Conversation

@mcremon-meta

@mcremon-meta mcremon-meta commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

Summary:
ConvToChannelsLast wraps every conv in permute(NCHW->NHWC) -> conv -> permute(NHWC->NCHW). When convs are joined by elementwise fused_quant ops
(residual add/mul, activations), permutes end up threaded through the
surrounding region and were never removed: the existing Cadence
RemovePermutesAroundElementwiseOps only recognizes aten/cadence elementwise
ops, not the SAS fused_quant ops -- and it would also wrongly treat their
lifted scale/zero_point operands as constants to be permuted.

This adds a fused_quant-aware permute-removal pass and wires it into the edge
optimization group:

  • Extend the shared ExecuTorch RemovePermutesAroundElementwiseOps with a
    small overridable seam (_permute_relevant_inputs) so a subclass can hide
    operands from layout propagation. Behavior-preserving for existing users.
  • New RemovePermutesAroundFusedQuantElementwiseOps (SAS) subclasses it,
    adding fused_quant.add/mul and the activation ops as permutable and
    exposing only their tensor operands, so the lifted scale/zero_point
    placeholders are never permuted/compensated (which would break lowering).
    Ops with per-channel qparams are skipped (not permutation-invariant).
  • Replace the two Cadence permute passes (which no-op pre-Lower) in the
    optimization group with this single pass.
  • Teach the shared subgraph engine about "permutation-sink" flattens: a
    view_copy whose input has <=1 non-unit dim (e.g. the [1, C, 1, 1] -> [1, C]
    after a global pool) is layout-invariant, so a permutation flowing into it
    simply dies. The region can terminate cleanly there with no compensating
    permute -- which lets the residual-block permutes collapse across the
    avgpool -> flatten -> classifier head instead of being stranded by it.

Note: fused_quant is currently SAS-specific, NOT yet a generic cross-backend
dialect, so the fused_quant knowledge deliberately stays in the SAS subclass
rather than the shared ExecuTorch pass. When fused_quant graduates to a shared
dialect, this can fold into the base pass via extra_permutable_ops + the
seam. (The permutation-sink flatten handling is generic and correctly lives in
the shared pass.)

On resnet18 the optimized graph goes from 83 permutes down to a single one (the
model-input boundary); every permute around the residual add/relu blocks and
across the global-pool flatten is removed.

Differential Revision: D113424191

cc @digantdesai @freddan80 @per @zingo @oscarandersson8218 @mansnils @Sebastian-Larsson @robell @rascani

Summary:
`ConvToChannelsLast` wraps every conv in `permute(NCHW->NHWC) -> conv ->
permute(NHWC->NCHW)`. When convs are joined by elementwise fused_quant ops
(residual add/mul, activations), permutes end up threaded through the
surrounding region and were never removed: the existing Cadence
`RemovePermutesAroundElementwiseOps` only recognizes aten/cadence elementwise
ops, not the SAS fused_quant ops -- and it would also wrongly treat their
lifted scale/zero_point operands as constants to be permuted.

This adds a fused_quant-aware permute-removal pass and wires it into the edge
optimization group:

- Extend the shared ExecuTorch `RemovePermutesAroundElementwiseOps` with a
  small overridable seam (`_permute_relevant_inputs`) so a subclass can hide
  operands from layout propagation. Behavior-preserving for existing users.
- New `RemovePermutesAroundFusedQuantElementwiseOps` (SAS) subclasses it,
  adding `fused_quant.add`/`mul` and the activation ops as permutable and
  exposing only their tensor operands, so the lifted scale/zero_point
  placeholders are never permuted/compensated (which would break lowering).
  Ops with per-channel qparams are skipped (not permutation-invariant).
- Replace the two Cadence permute passes (which no-op pre-Lower) in the
  optimization group with this single pass.
- Teach the shared subgraph engine about "permutation-sink" flattens: a
  `view_copy` whose input has <=1 non-unit dim (e.g. the `[1, C, 1, 1] -> [1, C]`
  after a global pool) is layout-invariant, so a permutation flowing into it
  simply dies. The region can terminate cleanly there with no compensating
  permute -- which lets the residual-block permutes collapse across the
  avgpool -> flatten -> classifier head instead of being stranded by it.

Note: fused_quant is currently SAS-specific, NOT yet a generic cross-backend
dialect, so the fused_quant knowledge deliberately stays in the SAS subclass
rather than the shared ExecuTorch pass. When fused_quant graduates to a shared
dialect, this can fold into the base pass via `extra_permutable_ops` + the
seam. (The permutation-sink flatten handling is generic and correctly lives in
the shared pass.)

On resnet18 the optimized graph goes from 83 permutes down to a single one (the
model-input boundary); every permute around the residual add/relu blocks and
across the global-pool flatten is removed.

Differential Revision: D113424191
@pytorch-bot

pytorch-bot Bot commented Jul 30, 2026

Copy link
Copy Markdown

🔗 Helpful Links

🧪 See artifacts and rendered test results at hud.pytorch.org/pr/pytorch/executorch/21481

Note: Links to docs will display an error until the docs builds have been completed.

⏳ No Failures, 65 Pending

As of commit 4a3b871 with merge base a1d4d35 (image):
💚 Looks good so far! There are no failures yet. 💚

This comment was automatically generated by Dr. CI and updates every 15 minutes.

@meta-cla meta-cla Bot added the CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. label Jul 30, 2026
@meta-codesync

meta-codesync Bot commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

@mcremon-meta has exported this pull request. If you are a Meta employee, you can view the originating Diff in D113424191.

@github-actions github-actions Bot added ciflow/trunk module: arm Issues related to arm backend labels Jul 30, 2026
@github-actions

Copy link
Copy Markdown

This PR needs a release notes: label

If your change should be included in the release notes (i.e. would users of this library care about this change?), please use a label starting with release notes:. This helps us keep track and include your important work in the next release notes.

To add a label, you can comment to pytorchbot, for example
@pytorchbot label "release notes: none"

For more information, see
https://github.com/pytorch/pytorch/wiki/PyTorch-AutoLabel-Bot#why-categorize-for-release-notes-and-how-does-it-work.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ciflow/trunk CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. meta-exported module: arm Issues related to arm backend

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant