Memory allocation on multiple return values

Is there any way to avoid allocations on functions that return multiple values, when some of these values are mutable?

For example:

julia> x = [0];
julia> fun1(x) = @time((x, 1));
julia> fun2(x) = (fun1(x); nothing);
julia> fun1(x)
  0.000000 seconds (1 allocation: 32 bytes)
([0], 1)
julia> fun2(x)
  0.000000 seconds

fun2 does not allocate memory in this case, but it does when fun1 is not inlined:

julia> @noinline fun1(x) = @time((x, 1));
julia> fun1(x)
  0.000000 seconds (1 allocation: 32 bytes)
([0], 1)
julia> fun2(x)
  0.000000 seconds (1 allocation: 32 bytes)

In my work, I need to return these preallocated objects for consistency with other methods defined for the same function.

2 Likes

Escaping structs with heap-allocated elements will also be heap allocated.
In the case of multiple return values, that means the tuple you’re constructing (the one holding all the returned values) must be heap allocated.

Given that you’re already preallocating, two options are:

  1. Don’t return the preallocated object. The caller must already have access to it.
  2. Preallocate objects with space for all the returned values, eg
mutable struct ArrayAndInt{T}
    x::Array{T}
    i::Int
end

then pass a preallocated instance, mutating i, and returning.

5 Likes

I’ve run into the same issue a few times. Although there are of multiple workarounds they nine are elegant. It would be very nice to have this fixed at the language level.

Most of the time the allocation doesn’t matter and one can just ignore it but occasionally it does make a difference. (And also for the typical “obsessive-compulsive” academic it is good for peace of mind to have zero allocations.)

Indeed, it would be great to have this optimization without having to sacrifice productivity. Thank you anyway.