Hi @Lilith
In the Gridap project we have ended with a systematic way of allocating buffers for function evaluation and also for array indexing. Take a look at this preprint for all specific details: [2109.12818] The software design of Gridap: a Finite Element package based on the Julia JIT compiler
For calling functions (or any callable object) we introduced this syntax:
cache = return_cache(f,x...)
fx = evaluate!(cache,f,x...)
for a callable object f
on the arguments in x
.
By default we define
return_cache(f,args...) = nothing
evaluate!(::Nothing,f,args...) = f(args...)
So, any callable object can be called via the exteded syntax and by default we do not use the cache and simply call the function on the given arguments as usual. However, we have opened the door to reuse data at each function call by specialising functions return_cache
and evaluate!
for particular cases.
For instance, consider this function f(x,y) = [x,y]
, just as a very simple example. If you don’t want to generate a new vector at each function call, you can define:
function return_cache(::typeof(f),x,y)
[x,y]
end
function evaluate!(cache,::typeof(f),x,y)
cache[1] = x
cache[2] = y
cache
end
and then you will get:
cache = return_cache(f,1,2) # New vector created here
fx = evaluate!(cache,f,10,20) # No vector created here
@assert fx == [10,20]
fx = evaluate!(cache,f,100,200) # No vector created here
@assert fx == [100,200]
This is essentially the idea. Note that this is thread safe since you can generate an independent buffer for each thread by calling return_cache
. Note also that one needs to be aware that the result fx
potentially takes ownership of some part of the state of the cache. So, one might need to generate several caches e.g.
c1 = return_cache(f,1,2)
c2 = return_cache(f,1,2)
for ((x1,y1),(x2,y2)) in zip([(1,2),(10,20)],[(10,20),(100,200)] )
fx1 = evaluate!(c1,f,x1,y1)
fx2 = evaluate!(c2,f,x2,y2)
@assert fx1 == 10*fx2
end
If we consider just a single cache, then fx1
and fx2
would be the same object.
This mechanism is pretty useful for our use cases and we are happy with it after been using it for a while.