Question about allocations and generators

Hello, this this question is out of curiosity, hoping to understand more about Julia.

Those three functions allocate differently:

g1(v::Vector{Int64}) = SVector{length(v), Int64}(v...)
g2(v::Vector{Int64}, n) = SVector{n, Int64}((v[i] for i=1:n)...)
g3(v::Vector{Int64}) = SVector{length(v), Int64}((v[i] for i=1:length(v))...)
julia> @benchmark g1($v) evals=1 samples=1
BenchmarkTools.Trial: 
  memory estimate:  640 bytes
  allocs estimate:  11

julia> @benchmark g2($v, 2) evals=1 samples=1
BenchmarkTools.Trial: 
  memory estimate:  0 bytes
  allocs estimate:  0

julia> @benchmark g3($v) evals=1 samples=1
BenchmarkTools.Trial: 
  memory estimate:  736 bytes
  allocs estimate:  14

Could someone guide me towards understanding this precisely? Thanks!

Allocations happen here because of Julia cannot infer the length of the SVector at compile time. When you pass in v and specify length(v) of the SVector, Julia can’t infer at compile time what length(v) is since it’s possible to change it’s length with functions like push!.

In your setup, g2 doesnt allocate because you’re not interpolating 2. Note that if you interpolate both arguments for g2,

julia> @btime g2($v,$2)
  1.242 μs (14 allocations: 736 bytes)

it allocates since Julia treats the $2 as a variable and not as a constant.

This is a sort of type instability - if you use @code_warntype, it shows you what Julia can’t infer by highlighting it in red.


Here, Julia can’t fully infer the type of the SVector output since the length is part of the type.

2 Likes

Note that all allocations come from not knowing the size of the output at compile time. If the size of the static vector to be created is known at compile time, the function does not allocate. This can be shown here, for example, in this case, where the size of the output is provided as a parameter of the type, even if the actual vector that the type contains is not static:

julia> struct Vec{N}
         v::Vector{Int}
       end

julia> g4(v::Vec{N}) where N = SVector{N,Int}(v.v[i] for i in 1:N)
g4 (generic function with 1 method)

julia> @btime g4(v) setup=(v=Vec{3}(rand(Int,3)))
  2.189 ns (0 allocations: 0 bytes)

(which means, more generally, that creating static vectors generally does not allocate if you can provide to the function where the allocations occur the size of the vectors as one parameter of the input types).

2 Likes