Preallocating and returning anonymous function

Hi, so I’m working on a project and I want to avoid many allocations called in a loop as is often suggested. That said I also want to avoid having to pass all the preallocated arrays/space/whatever it is at every function call. This is as I want this part of the code to be generic, in short I am evolving a field by calculating its derivative and then making a small step in its direction. My field is Q and I want my main code to be able to support any function dQdt!(dQdt, Q) which will set dQdt according to the passed Q field. However, many of these functions may be complex and require some additional memory.

Potentially another way to describe the problem is that I want to achieve something like an object which will carry its own memory and be able to be called while reusing that internal memory. Similar to how say the EigenSolver class works in the C++ library Eigen.

What I’ve come up with actually seems to work but I suspect there will be some drawbacks to it and so I want to ask about those and for any alternative method of achieving this goal. In short my method relies on defining another function which allocates the required memory and returns an anonymous function which calls the function that actually does the work. Here acting_on_A_using_B_as_buffer represents some function with complicated logic that needs an “internal” buffer B but is supplied it to, in order to be allocation free.

function acting_on_A_using_B_as_buffer(A, B)
    B .= transpose(A)
    A .= B .* A
end

function return_object_like_thing(A)
    B = similar(A)
    return x -> acting_on_A_using_B_as_buffer(x, B)
end

N = 100
A = rand(N, N);
B = rand(N, N);

@time acting_on_A_using_B_as_buffer(A, B); # has 0 allocs but need to supply B

@time f = return_object_like_thing(A); # seems to allocate correctly
@time f(A); # has 0 allocs, looks good?

This’ll work.

These are function-like objects. The mechanics of functions, including anonymous ones, are basically function-like objects with singleton types that have no fields and subtype Function, which does dispatch to some methods (affecting how it prints) and involves a non-specialization heuristic. This is why many higher order functions like map don’t dispatch on f::Function, to accommodate callable objects that don’t subtype Function.

2 Likes

Maybe a callable struct would be simpler here?

struct ActOnA{B}
  buffer::B
end

# define what happens when we use an ActOnA object like a function
function (b::ActOnA)(A)
  return acting_on_A_using_B_as_buffer(A, b.buffer)
end

act_on_A = ActOnA(similar(A)) # create buffer object
act_on_A(A) # call buffer object

Looks like the above poster beat me to this suggestion, but I’m still posting this for the snippet.

2 Likes

Also worth pointing out the smart move to decouple the buffer-allocating step (a simple similar for now) from instantiating ActonA. Now you don’t have to allocate a buffer for every instantiation, and an instantiation with a reused buffer ActonA(act_on_A.buffer) would return the same instance in fact (this would not be the case if there were more fields with different values).

1 Like

Wow, thank you both for such quick and good responses! I am surprised and impressed by Julia that this works as simple as that. And thank you for the struct suggestion, I am just learning the language and did not realize I could do such a thing. As mentioned, this has some advantages and I also feel like it’s a bit less repetitive than having to call the function itself repeating all the arguments.