Hello,
I discovered this behavior that created some weird allocations in my code. I feel like I get it now, but I would be interested in some relevant comments/explanations.
f_allocates(x) = isempty(x) ? nothing : (0, x)
f_does_not_allocate(x) = isempty(x) ? nothing : x
f_does_not_allocate_either(x) = (0, x)
The first one allocates when x is heap allocated.
The @code_warntype is not very explicit (or do I read it wrong?). The respective outputs are,
Variables
#self#::Core.Const(f_allocates)
x::Vector{Int64}
Body::Union{Nothing, Tuple{Int64, Vector{Int64}}}
1 β %1 = Main.isempty(x)::Bool
βββ goto #3 if not %1
2 β return Main.nothing
3 β %4 = Core.tuple(0, x)::Core.PartialStruct(Tuple{Int64, Vector{Int64}}, Any[Core.Const(0), Vector{Int64}])
βββ return %4
Variables
#self#::Core.Const(f_does_not_allocate)
x::Vector{Int64}
Body::Union{Nothing, Vector{Int64}}
1 β %1 = Main.isempty(x)::Bool
βββ goto #3 if not %1
2 β return Main.nothing
3 β return x
Variables
#self#::Core.Const(f_does_not_allocate_either)
x::Vector{Int64}
Body::Tuple{Int64, Vector{Int64}}
1 β %1 = Core.tuple(0, x)::Core.PartialStruct(Tuple{Int64, Vector{Int64}}, Any[Core.Const(0), Vector{Int64}])
βββ return %1
My understanding is that, the output of f_allocates
has to be heap allocated to count one more reference to its input. But this is not true for f_does_not_allocate_either
and f_does_not_allocate
thanks to some compiler optimizations. Is that correct?
How do those optimization work? I would be curious to understand if there is an intrinsic reason that makes f_allocates
non allocation free.
Thanks,