Skip to content

Improve integer aten_floor_divide export to drop Sign ops - #2960

Merged
Justin Chu (justinchuby) merged 3 commits into
mainfrom
copilot/better-onnx-export-aten-floor-divide
Aug 14, 2026
Merged

Improve integer aten_floor_divide export to drop Sign ops#2960
Justin Chu (justinchuby) merged 3 commits into
mainfrom
copilot/better-onnx-export-aten-floor-divide

Conversation

Copilot AI commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

The signed-integer path of aten_floor_divide converted truncation to flooring using !(sign(a) == sign(b)) && (bool)(a % b). This relies on two Sign ops (limited integral support on some EPs) and cannot be optimized when a is provably non-negative.

Change

  • aten_floor_divide sign-mismatch check (onnxscript/function_libs/torch_lib/ops/core.py): express signbit(a) != signbit(b) as (a < 0) == (b > 0) instead of !(Sign(a) == Sign(b)).
offset = op.And(
    op.Equal(op.Less(self, 0), op.Greater(other, 0)),
    op.Cast(op.Mod(self, other), to=BOOL.dtype),
)

Why

  • Removes both Sign ops (6 operators vs. 7).
  • When b is a known positive constant, the expression folds to (a < 0) && (bool)(a % b); a non-negative a (relu, argmax, sigmoid, …) then short-circuits the offset to false via (a < 0) alone — the real-model case from the issue.
  • (a < 0) == (b > 0) is equivalent to signbit(a) != signbit(b) for any valid nonzero divisor, since (b > 0) equals !signbit(b).

Copilot AI changed the title [WIP] Improve ONNX export for aten_floor_divide Improve integer aten_floor_divide export to drop Sign ops Jul 10, 2026
@justinchuby
Justin Chu (justinchuby) marked this pull request as ready for review July 10, 2026 17:32

Copilot AI 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.

Pull request overview

This PR updates the signed-integer export logic for aten_floor_divide to avoid using Sign ops in the “sign mismatch” detection used to convert truncating integer division into flooring division, improving EP compatibility and enabling better constant/range-driven graph simplifications.

Changes:

  • Replaces !(Sign(a) == Sign(b)) with an equivalent boolean predicate based on integer comparisons: (a < 0) == (b > 0).
  • Adds an explanatory comment documenting the intent (dropping Sign and aiding optimizer behavior).

Comment thread onnxscript/function_libs/torch_lib/ops/core.py Outdated
@codecov

codecov Bot commented Jul 10, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 72.57%. Comparing base (e6fad72) to head (74e3ab8).
⚠️ Report is 10 commits behind head on main.
✅ All tests successful. No failed tests found.

Additional details and impacted files
@@           Coverage Diff           @@
##             main    #2960   +/-   ##
=======================================
  Coverage   72.57%   72.57%           
=======================================
  Files         263      263           
  Lines       31957    31957           
  Branches     3007     3007           
=======================================
  Hits        23194    23194           
  Misses       7744     7744           
  Partials     1019     1019           

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
@justinchuby Justin Chu (justinchuby) added this to the 0.7.3 milestone Jul 10, 2026
@justinchuby

Copy link
Copy Markdown
Collaborator

Copilot which implementation is faster (cpu, gpu)?

Copilot AI commented Aug 13, 2026

Copy link
Copy Markdown
Contributor Author

Copilot which implementation is faster (cpu, gpu)?

Measured both formulations as standalone ONNX graphs (identical except for the sign-mismatch subgraph) with onnxruntime 1.28, CPU EP, INT64 inputs, 3 repeated runs:

shape Sign/Sign/Equal/Not Less/Greater/Equal
[1000] 0.023 / 0.023 / 0.018 ms 0.015 / 0.017 / 0.017 ms
[1000000] 17.55 / 18.02 / 16.50 ms 17.30 / 16.15 / 15.87 ms

So on CPU the new version is slightly faster (one fewer elementwise pass; small shapes benefit more from the lower node count, large shapes are memory-bandwidth-bound so the two are within noise of each other).

I don't have a GPU in this sandbox, so I can't give measured GPU numbers. Reasoning-wise the same argument applies more strongly there: both are memory-bound elementwise kernels, and the new form runs 3 kernels instead of 4 with one fewer intermediate tensor, so it should be at worst equal. The bigger practical win on GPU EPs is that Sign has limited integral type support on some EPs (causing fallback/partitioning), which this avoids entirely.

The main gain isn't raw op cost though — with a constant positive divisor the expression folds to (a < 0) && (bool)(a % b), and a provably non-negative a (relu/argmax/sigmoid outputs) lets the optimizer drop the whole offset computation, which the Sign-based form can't do.

@justinchuby
Justin Chu (justinchuby) merged commit ab2b5f4 into main Aug 14, 2026
31 of 34 checks passed
@justinchuby
Justin Chu (justinchuby) deleted the copilot/better-onnx-export-aten-floor-divide branch August 14, 2026 03:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Development

Successfully merging this pull request may close these issues.

Better ONNX export for aten_floor_divide in onnx script

4 participants