Skip to content

Fuse consecutive clamps into a single clamp (#21013)#21013

Open
apullin wants to merge 1 commit into
pytorch:mainfrom
apullin:export-D112351522
Open

Fuse consecutive clamps into a single clamp (#21013)#21013
apullin wants to merge 1 commit into
pytorch:mainfrom
apullin:export-D112351522

Conversation

@apullin

@apullin apullin commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

Summary:

ConvertToClampPass normalizes hardtanh, relu and relu6 into aten.clamp. Chains like HardTanh -> ReLU therefore become two adjacent clamps, the second of which is redundant.

Add FuseConsecutiveClampsPass, which folds any chain of adjacent (single-user) clamp.default ops into one clamp using exact composition: clamp(clamp(x,a,b),c,d) == clamp(x, max(a,c), min(b,d)). This subsumes HardTanh->ReLU, ReLU->ReLU, HardTanh->HardTanh, ReLU6->ReLU, explicit clamp->clamp, and longer chains, dropping one op (and one intermediate tensor) per fused pair.

Runs after FoldAndAnnotateQParamsPass so it fires in both the FP and INT paths; in the quantized case the surviving clamp inherits the first clamp's input qparams and the second's output qparams, dropping the intermediate requant (qtol=1, same tradeoff as FuseConsecutiveRescalesPass). Branching clamps (multiple users) are left untouched.

Differential Revision: D112351522

@apullin
apullin requested a review from digantdesai as a code owner July 17, 2026 15:02
@pytorch-bot

pytorch-bot Bot commented Jul 17, 2026

Copy link
Copy Markdown

🔗 Helpful Links

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

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

❌ 1 New Failure, 1 Pending, 1 Unrelated Failure, 1 Unclassified Failure

As of commit 6da120a with merge base 0dec2b6 (image):

NEW FAILURE - The following job has failed:

UNCLASSIFIED FAILURE - DrCI could not classify the following job because the workflow did not run on the merge base. The failure may be pre-existing on trunk or introduced by this PR:

BROKEN TRUNK - The following job failed but were present on the merge base:

👉 Rebase onto the `viable/strict` branch to avoid these failures

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 17, 2026
@github-actions github-actions Bot added ciflow/trunk module: arm Issues related to arm backend labels Jul 17, 2026
@meta-codesync

meta-codesync Bot commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

@apullin has exported this pull request. If you are a Meta employee, you can view the originating Diff in D112351522.

@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.

@rascani

rascani commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

Can you fix up the lintrunner? Thanks!

@apullin
apullin force-pushed the export-D112351522 branch from 9b482ed to f72666e Compare July 18, 2026 01:11
@meta-codesync meta-codesync Bot changed the title Fuse consecutive clamps into a single clamp Fuse consecutive clamps into a single clamp (#21013) Jul 18, 2026
apullin added a commit to apullin/executorch that referenced this pull request Jul 18, 2026
Summary:

ConvertToClampPass normalizes hardtanh, relu and relu6 into aten.clamp. Chains like HardTanh -> ReLU therefore become two adjacent clamps, the second of which is redundant.

Add FuseConsecutiveClampsPass, which folds any chain of adjacent (single-user) clamp.default ops into one clamp using exact composition: clamp(clamp(x,a,b),c,d) == clamp(x, max(a,c), min(b,d)). This subsumes HardTanh->ReLU, ReLU->ReLU, HardTanh->HardTanh, ReLU6->ReLU, explicit clamp->clamp, and longer chains, dropping one op (and one intermediate tensor) per fused pair.

Runs after FoldAndAnnotateQParamsPass so it fires in both the FP and INT paths; in the quantized case the surviving clamp inherits the first clamp's input qparams and the second's output qparams, dropping the intermediate requant (qtol=1, same tradeoff as FuseConsecutiveRescalesPass). Branching clamps (multiple users) are left untouched.

Differential Revision: D112351522
@apullin
apullin force-pushed the export-D112351522 branch from f72666e to 4a81b5a Compare July 18, 2026 15:15
apullin added a commit to apullin/executorch that referenced this pull request Jul 20, 2026
Summary:

ConvertToClampPass normalizes hardtanh, relu and relu6 into aten.clamp. Chains like HardTanh -> ReLU therefore become two adjacent clamps, the second of which is redundant.

Add FuseConsecutiveClampsPass, which folds any chain of adjacent (single-user) clamp.default ops into one clamp using exact composition: clamp(clamp(x,a,b),c,d) == clamp(x, max(a,c), min(b,d)). This subsumes HardTanh->ReLU, ReLU->ReLU, HardTanh->HardTanh, ReLU6->ReLU, explicit clamp->clamp, and longer chains, dropping one op (and one intermediate tensor) per fused pair.

Runs after FoldAndAnnotateQParamsPass so it fires in both the FP and INT paths; in the quantized case the surviving clamp inherits the first clamp's input qparams and the second's output qparams, dropping the intermediate requant (qtol=1, same tradeoff as FuseConsecutiveRescalesPass). Branching clamps (multiple users) are left untouched.

Differential Revision: D112351522
@apullin
apullin force-pushed the export-D112351522 branch from 4a81b5a to d97e61a Compare July 20, 2026 14:53
Summary:

ConvertToClampPass normalizes hardtanh, relu and relu6 into aten.clamp. Chains like HardTanh -> ReLU therefore become two adjacent clamps, the second of which is redundant.

Add FuseConsecutiveClampsPass, which folds any chain of adjacent (single-user) clamp.default ops into one clamp using exact composition: clamp(clamp(x,a,b),c,d) == clamp(x, max(a,c), min(b,d)). This subsumes HardTanh->ReLU, ReLU->ReLU, HardTanh->HardTanh, ReLU6->ReLU, explicit clamp->clamp, and longer chains, dropping one op (and one intermediate tensor) per fused pair.

Runs after FoldAndAnnotateQParamsPass so it fires in both the FP and INT paths; in the quantized case the surviving clamp inherits the first clamp's input qparams and the second's output qparams, dropping the intermediate requant (qtol=1, same tradeoff as FuseConsecutiveRescalesPass). Branching clamps (multiple users) are left untouched.

Differential Revision: D112351522
@apullin
apullin force-pushed the export-D112351522 branch from d97e61a to 6da120a Compare July 20, 2026 16:13
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.

2 participants