How to support passing temporary buffers around

You might want to have a look at AutoPreallocation.jl.

7 Likes

@fverdugo - Iā€™ve been doing something very very similar in my own codes for a while. Iā€™ll need to look at Gridap to compare. Do I understand correctly that the callable f would need to keep an object pool it returns the cache to? One problem that I encountered and havenā€™t really solved is the case when the type of the cache depends on the type of the input data. E.g. this might happen if I evaluate with a Float or with a Dual for ForwardDiff. How do you solve this?

Independently - something Iā€™m playing with right now, is to attach this object pool to the cache array and then call release! on it without having to remember where it is from :). Something like this:

struct CachedArray{N, T}
   A::Array{N, T}
   pool::ArrayCache{T}
end

release!(pA::CachedArray) = release!(pA.pool, pA)

Base.getindex(pA::CachedArray, args...) = getindex(pA.A, args...)

Then a callable object f would return a A::CachedArray, which the caller via A = evaluate(f, ...) and when done it can be released via release!(A) anywhere without needed to remember where A came from. This seems to simplify my codes quite a bit, but I havenā€™t extensively tested the idea yet, so might still run into problems.

Hi @cortner! We solve this by parametrizing the cache creation also with the inputs of the function:

cache = return_cache(f,x...)
fx = evaluate!(cache,f,x...)

Then the user code is consistent with this invariant: The cache object created for args of types as in xā€¦ can only be used when calling the function with args of the same type. Note that the cache creation only needs info about the types of the args xā€¦ in general.

Since this mechanism is quite general purpose, I am considering to factor this interface out of Gridap and release it in a separate package with almost no dependencies. Perhaps it will be interesting for others.

Ive used this mechanism as well but it is still problematic when you are composing several layers of function calls and makes for very ugly codes trying to predict the occurring types along the way. It could be context specific how generally useful it is. Iā€™ve certainly found this worked very well indeed in many cases.