Improve integer aten_floor_divide export to drop Sign ops - #2960
Conversation
There was a problem hiding this comment.
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
Signand aiding optimizer behavior).
Codecov Report✅ All modified and coverable lines are covered by tests. 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. |
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
|
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:
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 The main gain isn't raw op cost though — with a constant positive divisor the expression folds to |
The signed-integer path of
aten_floor_divideconverted truncation to flooring using!(sign(a) == sign(b)) && (bool)(a % b). This relies on twoSignops (limited integral support on some EPs) and cannot be optimized whenais provably non-negative.Change
aten_floor_dividesign-mismatch check (onnxscript/function_libs/torch_lib/ops/core.py): expresssignbit(a) != signbit(b)as(a < 0) == (b > 0)instead of!(Sign(a) == Sign(b)).Why
Signops (6 operators vs. 7).bis a known positive constant, the expression folds to(a < 0) && (bool)(a % b); a non-negativea(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 tosignbit(a) != signbit(b)for any valid nonzero divisor, since(b > 0)equals!signbit(b).