Why does `Union{Int,...}` in a `Vector` not cause allocations but `Union{Float64,...}` does?

jules
Wed, 03 Sep 2025 07:36:36 +0000
Performance

I have a custom missing type which stores a symbol and a reference to an object whose type is not predetermined, so it’s an Any field. I need to store vectors of these, some values will be missing but most won’t be. Here’s a test function that looks at the allocations that happen when writing values and missings into such a vector.

The peculiar thing to me is that when I write ten missings unioned with Int, I get around 10 allocations, so one per missing. But if I union with Float64, every value in the vector needs an allocation. Why is the storage behavior different if both Int and Float64 are primitives with 8 bytes size?

struct MyMissing
  location::Symbol
  reason
end

function missing_vector(T, n)
  v = Vector{Union{MyMissing,T}}(undef, n)
  for i in 1:n
    v[i] = i > 10 ? T(i) : MyMissing(:missing_vector, "under ten")
  end
  return count(x -> x isa MyMissing, v)
end

julia> @time missing_vector(Int, 100)
  0.000008 seconds (12 allocations: 1.219 KiB)
10

julia> @time missing_vector(Float64, 100)
  0.000018 seconds (102 allocations: 2.625 KiB)
10

9 posts - 5 participants