Skip to content

Narrow Zero arithmetic methods to reduce invalidations#348

Merged
odow merged 1 commit intojump-dev:masterfrom
oameye:fix/reduce-zero-invalidations
Mar 31, 2026
Merged

Narrow Zero arithmetic methods to reduce invalidations#348
odow merged 1 commit intojump-dev:masterfrom
oameye:fix/reduce-zero-invalidations

Conversation

@oameye
Copy link
Copy Markdown
Contributor

@oameye oameye commented Mar 27, 2026

Summary

  • Narrow +(x::Any, ::Zero), *(::Zero, ::Any), -(x::Any, ::Zero),
    and /(::Zero, x::Any) from ::Any to Number, AbstractArray, and AbstractMutable
  • Moves AbstractMutable methods to dispatch.jl (where the type is defined)
  • All existing tests pass

Motivation

Loading MutableArithmetics causes 1,489 method invalidations. After this PR: 1,317 (-172).

The remaining MA-owned invalidations (462) are from MutatingStepRange <: OrdinalRange which is inherent to subtyping OrdinalRange and not addressable by type narrowing. The rest (855) come from SparseArrays/Test stdlibs.

Before (v1.6.7 released)

Method Invalidations
+(x::Any, ::Zero) 446
step(r::MutatingStepRange) 429
*(::Zero, ::Any) 56
iterate(r::MutatingStepRange, i) 30

After (this PR)

Method Invalidations
step(r::MutatingStepRange) 429 (unchanged — inherent to OrdinalRange subtyping)
iterate(r::MutatingStepRange, i) 30 (unchanged)
+(x::Number, ::Zero) 0
*(::Number, z::Zero) 0

The Zero +/*/-// invalidations are fully eliminated by this PR. The MutatingStepRange invalidations remain but are a separate issue.

How to reproduce

In a fresh Julia session:

using SnoopCompileCore
invs = @snoop_invalidations begin
    using MutableArithmetics
end
using SnoopCompile
using SnoopCompile: countchildren
trees = invalidation_trees(invs)
trees = filter(t -> countchildren(t) > 0, trees)
sort!(trees; by=countchildren, rev=true)
println("Total invalidated methods: ", sum(countchildren, trees))
for t in trees
    println("  $(countchildren(t)) children — ", t)
end

Test plan

  • All existing MutableArithmetics tests pass
  • Invalidation count verified with SnoopCompile before/after

The methods +(x::Any, ::Zero), *(::Zero, ::Any), etc. used untyped
arguments, causing method invalidations by superseding the fundamental
+(x, y) and *(x, y) fallbacks in Base.

Narrow these to Number, AbstractArray, and AbstractMutable since
these cover the types that participate in MutableArithmetics rewrites.
Downstream packages with custom types can define their own
+(::MyType, ::Zero) methods (as MultivariatePolynomials already does).
@codecov
Copy link
Copy Markdown

codecov bot commented Mar 30, 2026

Codecov Report

❌ Patch coverage is 57.89474% with 8 lines in your changes missing coverage. Please review.
✅ Project coverage is 91.40%. Comparing base (6ca6d1b) to head (ebb9ae6).
⚠️ Report is 3 commits behind head on master.

Files with missing lines Patch % Lines
src/rewrite.jl 61.53% 5 Missing ⚠️
src/dispatch.jl 50.00% 3 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master     #348      +/-   ##
==========================================
- Coverage   91.79%   91.40%   -0.40%     
==========================================
  Files          23       23              
  Lines        2292     2304      +12     
==========================================
+ Hits         2104     2106       +2     
- Misses        188      198      +10     

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

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@odow
Copy link
Copy Markdown
Member

odow commented Mar 31, 2026

This seems reasonable, but @blegat might need to chime in. I'll look into the allocation issues in a separate PR.

@odow odow merged commit 85a2ffb into jump-dev:master Mar 31, 2026
7 of 11 checks passed
@nsajko
Copy link
Copy Markdown
Contributor

nsajko commented Apr 1, 2026

👏

@oameye
Copy link
Copy Markdown
Contributor Author

oameye commented Apr 1, 2026

:)

Base.:*(z::Zero) = z

function Base.:/(z::Zero, x::Any)
function Base.:/(z::Zero, x::Number)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we can add integration tests for downstream packages to CI?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, we have something similar for MathOptInterface

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@blegat
Copy link
Copy Markdown
Member

blegat commented Apr 2, 2026

The MutatingStepRange step range invalidations are a bit unfortunate. Actually I developed it more because I was curious to make it work but I don't know if any package is using it, I should have probably developed it in a separate package, but that would be breaking now... (in practice maybe not too breaking if no one is using it though)

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

Labels

None yet

Development

Successfully merging this pull request may close these issues.

4 participants