Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Does MutableDiffResult have to exist at all? #26

Open
gdalle opened this issue Mar 15, 2024 · 4 comments
Open

Does MutableDiffResult have to exist at all? #26

gdalle opened this issue Mar 15, 2024 · 4 comments

Comments

@gdalle
Copy link
Member

gdalle commented Mar 15, 2024

Preliminary: DiffResult objects must always be realiased when updated, as mentioned in the docs

https://juliadiff.org/DiffResults.jl/stable/#DiffResults.value!

In most cases it is unnecessary, because the result object itself gets mutated. However, it causes a heap allocation to create the mutable struct defined here:

mutable struct MutableDiffResult{O,V,D<:Tuple} <: DiffResult{O,V,D}
value::V
derivs::D # ith element = ith-order derivative
function MutableDiffResult(value::V, derivs::NTuple{O,Any}) where {O,V}
return new{O,V,typeof(derivs)}(value, derivs)
end
end

Here's a proof of concept: instead of the current code

value!(r::MutableDiffResult, x::Number) = (r.value = x; return r)
value!(r::MutableDiffResult, x::AbstractArray) = (copyto!(value(r), x); return r)

we could instead return a new object every time

value!(r::MutableDiffResult, x::Number) = MutableDiffResult(x, r.derivs)
value!(r::MutableDiffResult, x::AbstractArray) = (copyto!(value(r), x); return r)
  • Would it decrease or increase the performance of the update? Don't know
  • Would it be considered a breaking change? Perhaps (see below)
  • Would it break stuff in practice? Definitely (see below)
@gdalle
Copy link
Member Author

gdalle commented Mar 15, 2024

In terms of SemVer, I'm not sure whether the mutable/immutable distinction is part of the API. It is mentioned in a few docstrings but the concrete types themselves have no docstrings

https://juliadiff.org/DiffResults.jl/stable/#DiffResults.DiffResult

I know that some dependencies (like ForwardDiff and ReverseDiff) break if we decide to return a new struct in more cases, because they don't follow the documentation's advice on realiasing.
But then again they misuse the interface so it's pretty much their fault, right?

Here's an issue to keep track of these miscreants:

@ChrisRackauckas
Copy link
Member

Would it decrease or increase the performance of the update? Don't know

Yes, there is an ImmutableDiffResult and if you update the package to use that you see resulting heap allocations.

Would it be considered a breaking change? Perhaps (see below)

Yes

Would it break stuff in practice? Definitely (see below)

Yes, there are many SciML examples use it for mutation and ignore the return.

@gdalle
Copy link
Member Author

gdalle commented Mar 15, 2024

Not sure i understand your answer to the performance aspect, I asked whether it would be good or bad and you said yes ^^

The SciML packages that use results without realiasing are already incorrect for StaticArrays, even without changes in DiffResults. Example: JuliaDiff/ReverseDiff.jl#251 (comment)
In this regard, wouldnt it make sense to fix those used anyway?

@ChrisRackauckas
Copy link
Member

No, those would use the immutable diff results and a different code path.

Not sure i understand your answer to the performance aspect, I asked whether it would be good or bad and you said yes ^^

It causes allocations last time I checked. In certain scenarios it can elide but often it does not.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants