Filling pre-allocated arrays with minimal function allocations

Hello. I am writing some in which I take a time series of some coordinates x, y, z and must transform them to another coordinate system via some set of functions X = X(x, y, z), Y = Y(x, y, z) and Z = Z(x, y, z). I would like to do this with minimal memory allocations. As a first step in trying to optimise my code, I would like to understand why the following function is not zero allocating:

using BenchmarkTools
function func!(x1::AbstractVector{Float64}, x0::AbstractVector{Float64})
    x1[1] = x0[1]
    x1[2] = x0[2]
    x1[3] = x0[3]
end

x1 = zeros(3)
x2 = ones(3)

@btime func!(x1, x2);

which gives the following output

julia> @btime func!(x1, x2);
  19.027 ns (1 allocation: 16 bytes)

I thought that since I have pre-allocated the arrays, there would be no allocations here. Could someone explain why this is not the case?

You should put $ signs before your variables when you want to pass them to @btime like this:

julia> @btime func!($x1, $x2)
  3.472 ns (0 allocations: 0 bytes)

In general you should make your global variabls const as much as possible so the compilers can make better optimizations:

const x3 = zeros(3)
const x4 = ones(3)

julia> @btime func!(x3, x4) # notice variables without $ sign
  3.460 ns (0 allocations: 0 bytes) 
1 Like

Ah, I see. So the function is indeed zero-allocating, but I wan’t profiling the function correctly with the arguments of btime(). Thank you.

1 Like

For the special case of 3-vectors (or other relatively small arrays with a constant size that is less than ~100 elements) you should strongly consider StaticArrays.jl. In particular, SVector is non-allocating (not even initially) and even faster (at small sizes) than Vector.

SVector is not mutable (MVector is, but is usually worse than SVector), so your code above wouldn’t work as written. But I find that code written with immutables is usually “nicer” and more performant than mutating code.

1 Like