# Strange memory allocations

I have the following function:

``````function residual!(res, yd, y::MVector{S, SimFloat}, s::KPS3, time) where S
if false
T = S-2 # T: three times the number of particles excluding the origin
segments = div(T,6) - KITE_PARTICLES
# unpack the vectors y and yd
# extract the data for the winch simulation
length,  v_reel_out  = y[end-1],  y[end]
lengthd, v_reel_outd = yd[end-1], yd[end]
# extract the data of the particles
y_  = @view y[1:end-2]
yd_ = @view yd[1:end-2]
# unpack the vectors y and yd
part  = reshape(SVector{T}(y_),  Size(3, div(T,6), 2))
partd = reshape(SVector{T}(yd_), Size(3, div(T,6), 2))
#     pos1 = part[:,:,1]
#     pos1, vel1 = part[:,:,1], part[:,:,2]
#     pos = SVector{div(T,6)+1}(if i==1 SVector(0.0,0,0) else SVector(pos1[:,i-1]) end for i in 1:div(T,6)+1)
#     vel = SVector{div(T,6)+1}(if i==1 SVector(0.0,0,0) else SVector(vel1[:,i-1]) end for i in 1:div(T,6)+1)
#     posd1, veld1 = partd[:,:,1], partd[:,:,2]
#     posd = SVector{div(T,6)+1}(if i==1 SVector(0.0,0,0) else SVector(posd1[:,i-1]) end for i in 1:div(T,6)+1)
#     veld = SVector{div(T,6)+1}(if i==1 SVector(0.0,0,0) else SVector(veld1[:,i-1]) end for i in 1:div(T,6)+1)
else
part = reshape(SVector{S}(y),  Size(3, div(S,6), 2))
partd = reshape(SVector{S}(yd),  Size(3, div(S,6), 2))
pos1, vel1 = part[:,:,1], part[:,:,2]
pos = SVector{div(S,6)+1}(if i==1 SVector(0.0,0,0) else SVector(pos1[:,i-1]) end for i in 1:div(S,6)+1)
vel = SVector{div(S,6)+1}(if i==1 SVector(0.0,0,0) else SVector(vel1[:,i-1]) end for i in 1:div(S,6)+1)
posd1, veld1 = partd[:,:,1], partd[:,:,2]
posd = SVector{div(S,6)+1}(if i==1 SVector(0.0,0,0) else SVector(posd1[:,i-1]) end for i in 1:div(S,6)+1)
veld = SVector{div(S,6)+1}(if i==1 SVector(0.0,0,0) else SVector(veld1[:,i-1]) end for i in 1:div(S,6)+1)
end

# update parameters
pos_kite = pos[div(S,6)+1]
s.vel_kite .= vel[div(S,6)+1]
delta_t = time - s.t_0
delta_v = s.v_reel_out - s.last_v_reel_out
s.segment_length = (s.l_tether + s.last_v_reel_out * delta_t + 0.5 * delta_v * delta_t^2) / div(S,6)
s.c_spring = s.set.c_spring / s.segment_length
s.damping  = s.set.damping / s.segment_length
s.beta = calc_elevation(s)

# call core calculation routines
vec_c = SVector{3, SimFloat}(pos[s.set.segments] - pos_kite)     # convert to SVector to avoid allocations
v_app = SVector{3, SimFloat}(s.v_wind - s.vel_kite)
calc_set_cl_cd!(s, vec_c, v_app)
calc_aero_forces(s, pos_kite, s.vel_kite, s.rho, s.steering) # force at the kite
loop(s, pos, vel, posd, veld, s.res1, s.res2)

# copy and flatten result
for i in 2:div(S,6)+1
for j in 1:3
@inbounds res[3*(i-2)+j]              = s.res1[i][j]
@inbounds res[3*(div(S,6))+3*(i-2)+j] = s.res2[i][j]
end
end
if norm(res) < 1e5
# println(norm(res))
for i in eachindex(pos)
@inbounds s.pos[i] .= pos[i]
end
end
# @assert ! isnan(norm(res))
s.iter += 1
nothing
end
``````

It does not allocate any memory. If I uncomment the line `# pos1 = part[:,:,1]` it suddenly allocates a lot of memory, even though this part of the code is never executed. How can that happen?

The full test can be found here: KiteModels.jl/bench3.jl at main Β· ufechner7/KiteModels.jl Β· GitHub

Does the uncommenting make it type unstable? What does `@code_warntype` say?

Is getindex of SVector non-allocating?

Simplified test case:

``````using StaticArrays, LinearAlgebra

const KVec3    = MVector{3, Float64}

Base.@kwdef mutable struct KPS3{S, T, P}
v_apparent::T =       zeros(S, 3)
end

const kps3= KPS3{Float64, KVec3, 6+4+1}()
const KITE_PARTICLES = 4

const WINCH = false

function residual!(res, yd, y::MVector{S, Float64}, s::KPS3, time) where S
if WINCH
T = S-2 # T: three times the number of particles excluding the origin
segments = div(T,6) - KITE_PARTICLES
length,  v_reel_out  = y[end-1],  y[end]
lengthd, v_reel_outd = yd[end-1], yd[end]
# extract the data of the particles
y_  = @view y[1:end-2]
yd_ = @view yd[1:end-2]
part  = reshape(SVector{T}(y_),  Size(3, div(T,6), 2))
partd = reshape(SVector{T}(yd_), Size(3, div(T,6), 2))
# if you comment the following line there are no allocations
pos1 = part[:,:,1]
else
part = reshape(SVector{S}(y),  Size(3, div(S,6), 2))
partd = reshape(SVector{S}(yd),  Size(3, div(S,6), 2))
pos1, vel1 = part[:,:,1], part[:,:,2]
pos = SVector{div(S,6)+1}(if i==1 SVector(0.0,0,0) else SVector(pos1[:,i-1]) end for i in 1:div(S,6)+1)
vel = SVector{div(S,6)+1}(if i==1 SVector(0.0,0,0) else SVector(vel1[:,i-1]) end for i in 1:div(S,6)+1)
posd1, veld1 = partd[:,:,1], partd[:,:,2]
posd = SVector{div(S,6)+1}(if i==1 SVector(0.0,0,0) else SVector(posd1[:,i-1]) end for i in 1:div(S,6)+1)
veld = SVector{div(S,6)+1}(if i==1 SVector(0.0,0,0) else SVector(veld1[:,i-1]) end for i in 1:div(S,6)+1)
end
nothing
end

function test_residual()
if WINCH
y0 = MVector{62, Float64}([13.970413450119487, 0.0, 21.238692070636343, 27.65581376097752, 0.0, 42.66213714321849, 40.976226230518435, 0.0, 64.314401166278, 53.87184032029182, 0.0, 86.22231803750196, 66.28915240374937, 0.0, 108.4048292516046, 78.17713830204762, 0.0, 130.87545423106485, 79.56930502428155, 0.0, 135.70836376062155, 80.90383289255747, 0.0, 137.7696816741141, 80.60126812407692, 2.4016533873456325, 135.3023287520457, 80.60126812407692, -2.4016533873456325, 135.3023287520457, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 150.0, 0.0])
yd0= MVector{62, Float64}([0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -9.81, 0.0, 0.0, -9.81, 0.0, 0.0, -9.81, 0.0, 0.0, -9.81, 0.0, 0.0, -9.81, 0.0, 0.0, -9.81, 0.0, 0.0, -9.81, 0.0, 0.0, -9.81, 0.0, 0.0, -9.81, 0.0, 0.0, -9.81, 0.0, 0.0])
else
y0 = MVector{60, Float64}([13.970413450119487, 0.0, 21.238692070636343, 27.65581376097752, 0.0, 42.66213714321849, 40.976226230518435, 0.0, 64.314401166278, 53.87184032029182, 0.0, 86.22231803750196, 66.28915240374937, 0.0, 108.4048292516046, 78.17713830204762, 0.0, 130.87545423106485, 79.56930502428155, 0.0, 135.70836376062155, 80.90383289255747, 0.0, 137.7696816741141, 80.60126812407692, 2.4016533873456325, 135.3023287520457, 80.60126812407692, -2.4016533873456325, 135.3023287520457, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0])
yd0= MVector{60, Float64}([0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -9.81, 0.0, 0.0, -9.81, 0.0, 0.0, -9.81, 0.0, 0.0, -9.81, 0.0, 0.0, -9.81, 0.0, 0.0, -9.81, 0.0, 0.0, -9.81, 0.0, 0.0, -9.81, 0.0, 0.0, -9.81, 0.0, 0.0, -9.81])
end
time = 0.1
res1 = zeros(SVector{6, KVec3})
res2 = deepcopy(res1)
res = reduce(vcat, vcat(res1, res2))
# call first time to compile
residual!(res, yd0, y0, kps3, time)
# measure allocations
@allocated residual!(res, yd0, y0, kps3, time)
end

test_residual()
``````

This shows 1728 allocation which disappear if I comment the line `pos1 = part[:,:,1]` .

The allocations also disappear if I use `WINCH=true`.

So all works fine if I execute the code that I want to execute with the correct sized array as input.

It allocates if there is dead code inside an `if false; ...; end` block that would operate on a wrongly sized array if executed.

Not sure if this is a bugβ¦

``````julia> @code_warntype test_residual()
MethodInstance for test_residual()
from test_residual() in Main at /home/ufechner/repos/KiteModels/test/test_residual2.jl:40
Arguments
#self#::Core.Const(test_residual)
Locals
b1::Base.RefValue{Int64}
b0::Base.RefValue{Int64}
res::MVector{36, Float64}
res2::SVector{6, MVector{3, Float64}}
res1::SVector{6, MVector{3, Float64}}
time::Float64
yd0::MVector{60, Float64}
y0::MVector{60, Float64}
Body::Int64
1 β       Core.NewvarNode(:(b1))
β         Core.NewvarNode(:(b0))
β         Core.NewvarNode(:(res))
β         Core.NewvarNode(:(res2))
β         Core.NewvarNode(:(res1))
β         Core.NewvarNode(:(time))
β         Core.NewvarNode(:(yd0))
β         Core.NewvarNode(:(y0))
βββ       goto #3 if not Main.WINCH
2 β       Core.Const(:(Core.apply_type(Main.MVector, 62, Main.Float64)))
β         Core.Const(:(Base.vect(13.970413450119487, 0.0, 21.238692070636343, 27.65581376097752, 0.0, 42.66213714321849, 40.976226230518435, 0.0, 64.314401166278, 53.87184032029182, 0.0, 86.22231803750196, 66.28915240374937, 0.0, 108.4048292516046, 78.17713830204762, 0.0, 130.87545423106485, 79.56930502428155, 0.0, 135.70836376062155, 80.90383289255747, 0.0, 137.7696816741141, 80.60126812407692, 2.4016533873456325, 135.3023287520457, 80.60126812407692, -2.4016533873456325, 135.3023287520457, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 150.0, 0.0)))
β         Core.Const(:(y0 = (%10)(%11)))
β         Core.Const(:(Core.apply_type(Main.MVector, 62, Main.Float64)))
β         Core.Const(:(Base.vect(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -9.81, 0.0, 0.0, -9.81, 0.0, 0.0, -9.81, 0.0, 0.0, -9.81, 0.0, 0.0, -9.81, 0.0, 0.0, -9.81, 0.0, 0.0, -9.81, 0.0, 0.0, -9.81, 0.0, 0.0, -9.81, 0.0, 0.0, -9.81, 0.0, 0.0)))
β         Core.Const(:(yd0 = (%13)(%14)))
βββ       Core.Const(:(goto %23))
3 β %17 = Core.apply_type(Main.MVector, 60, Main.Float64)::Core.Const(MVector{60, Float64})
β   %18 = Base.vect(13.970413450119487, 0.0, 21.238692070636343, 27.65581376097752, 0.0, 42.66213714321849, 40.976226230518435, 0.0, 64.314401166278, 53.87184032029182, 0.0, 86.22231803750196, 66.28915240374937, 0.0, 108.4048292516046, 78.17713830204762, 0.0, 130.87545423106485, 79.56930502428155, 0.0, 135.70836376062155, 80.90383289255747, 0.0, 137.7696816741141, 80.60126812407692, 2.4016533873456325, 135.3023287520457, 80.60126812407692, -2.4016533873456325, 135.3023287520457, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0)::Vector{Float64}
β         (y0 = (%17)(%18))
β   %20 = Core.apply_type(Main.MVector, 60, Main.Float64)::Core.Const(MVector{60, Float64})
β   %21 = Base.vect(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -9.81, 0.0, 0.0, -9.81, 0.0, 0.0, -9.81, 0.0, 0.0, -9.81, 0.0, 0.0, -9.81, 0.0, 0.0, -9.81, 0.0, 0.0, -9.81, 0.0, 0.0, -9.81, 0.0, 0.0, -9.81, 0.0, 0.0, -9.81)::Vector{Float64}
β         (yd0 = (%20)(%21))
β         (time = 0.1)
β   %24 = Core.apply_type(Main.SVector, 6, Main.KVec3)::Core.Const(SVector{6, MVector{3, Float64}})
β         (res1 = Main.zeros(%24))
β         (res2 = Main.deepcopy(res1))
β   %27 = Main.vcat(res1, res2)::SVector{12, MVector{3, Float64}}
β         (res = Main.reduce(Main.vcat, %27))
β         Main.residual!(res, yd0, y0, Main.kps3, time::Core.Const(0.1))
β         \$(Expr(:meta, :force_compile))
β   %31 = Core.apply_type(Base.Ref, Base.Int64)::Core.Const(Ref{Int64})
β         (b0 = (%31)(0))
β   %33 = Core.apply_type(Base.Ref, Base.Int64)::Core.Const(Ref{Int64})
β         (b1 = (%33)(0))
β         Base.gc_bytes(b0)
β         Main.residual!(res, yd0, y0, Main.kps3, time::Core.Const(0.1))
β         Base.gc_bytes(b1)
β   %38 = Base.getindex(b1)::Int64
β   %39 = Base.getindex(b0)::Int64
β   %40 = (%38 - %39)::Int64
βββ       return %40
``````

I would not know what to look for, thoughβ¦

Its a bit annoying in the sense that I cannot execute different branches of my code depending on a constant without causing allocationsβ¦

Created a bug report: Dead code is causing memory allocations Β· Issue #48798 Β· JuliaLang/julia Β· GitHub

OK, this is a duplicate of performance of captured variables in closures Β· Issue #15276 Β· JuliaLang/julia Β· GitHub .

Their is an easy workaround: Just rename some of the variables that appear in both branches of the code:

``````using StaticArrays, LinearAlgebra

const KVec3    = MVector{3, Float64}

Base.@kwdef mutable struct KPS3{S, T, P}
v_apparent::T =       zeros(S, 3)
end

const kps3= KPS3{Float64, KVec3, 6+4+1}()
const KITE_PARTICLES = 4

const WINCH = false

function residual!(res, yd, y::MVector{S, Float64}, s::KPS3, time) where S
if WINCH
T = S-2 # T: three times the number of particles excluding the origin
segments = div(T,6) - KITE_PARTICLES
length,  v_reel_out  = y[end-1],  y[end]
lengthd, v_reel_outd = yd[end-1], yd[end]
# extract the data of the particles
y_  = @view y[1:end-2]
yd_ = @view yd[1:end-2]
part  = reshape(SVector{T}(y_),  Size(3, div(T,6), 2))
partd = reshape(SVector{T}(yd_), Size(3, div(T,6), 2))
# if you comment the following line there are no allocations
pos1_, vel1_ = part[:,:,1], part[:,:,2]
pos = SVector{div(T,6)+1}(if i==1 SVector(0.0,0,0) else SVector(pos1_[:,i-1]) end for i in 1:div(T,6)+1)
vel = SVector{div(T,6)+1}(if i==1 SVector(0.0,0,0) else SVector(vel1_[:,i-1]) end for i in 1:div(T,6)+1)
posd1_, veld1_ = partd[:,:,1], partd[:,:,2]
posd = SVector{div(T,6)+1}(if i==1 SVector(0.0,0,0) else SVector(posd1_[:,i-1]) end for i in 1:div(T,6)+1)
veld = SVector{div(T,6)+1}(if i==1 SVector(0.0,0,0) else SVector(veld1_[:,i-1]) end for i in 1:div(T,6)+1)
else
part = reshape(SVector{S}(y),  Size(3, div(S,6), 2))
partd = reshape(SVector{S}(yd),  Size(3, div(S,6), 2))
pos1, vel1 = part[:,:,1], part[:,:,2]
pos = SVector{div(S,6)+1}(if i==1 SVector(0.0,0,0) else SVector(pos1[:,i-1]) end for i in 1:div(S,6)+1)
vel = SVector{div(S,6)+1}(if i==1 SVector(0.0,0,0) else SVector(vel1[:,i-1]) end for i in 1:div(S,6)+1)
posd1, veld1 = partd[:,:,1], partd[:,:,2]
posd = SVector{div(S,6)+1}(if i==1 SVector(0.0,0,0) else SVector(posd1[:,i-1]) end for i in 1:div(S,6)+1)
veld = SVector{div(S,6)+1}(if i==1 SVector(0.0,0,0) else SVector(veld1[:,i-1]) end for i in 1:div(S,6)+1)
end
nothing
end

function test_residual()
if WINCH
y0 = MVector{62, Float64}([13.970413450119487, 0.0, 21.238692070636343, 27.65581376097752, 0.0, 42.66213714321849, 40.976226230518435, 0.0, 64.314401166278, 53.87184032029182, 0.0, 86.22231803750196, 66.28915240374937, 0.0, 108.4048292516046, 78.17713830204762, 0.0, 130.87545423106485, 79.56930502428155, 0.0, 135.70836376062155, 80.90383289255747, 0.0, 137.7696816741141, 80.60126812407692, 2.4016533873456325, 135.3023287520457, 80.60126812407692, -2.4016533873456325, 135.3023287520457, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 150.0, 0.0])
yd0= MVector{62, Float64}([0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -9.81, 0.0, 0.0, -9.81, 0.0, 0.0, -9.81, 0.0, 0.0, -9.81, 0.0, 0.0, -9.81, 0.0, 0.0, -9.81, 0.0, 0.0, -9.81, 0.0, 0.0, -9.81, 0.0, 0.0, -9.81, 0.0, 0.0, -9.81, 0.0, 0.0])
else
y0 = MVector{60, Float64}([13.970413450119487, 0.0, 21.238692070636343, 27.65581376097752, 0.0, 42.66213714321849, 40.976226230518435, 0.0, 64.314401166278, 53.87184032029182, 0.0, 86.22231803750196, 66.28915240374937, 0.0, 108.4048292516046, 78.17713830204762, 0.0, 130.87545423106485, 79.56930502428155, 0.0, 135.70836376062155, 80.90383289255747, 0.0, 137.7696816741141, 80.60126812407692, 2.4016533873456325, 135.3023287520457, 80.60126812407692, -2.4016533873456325, 135.3023287520457, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0])
yd0= MVector{60, Float64}([0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -9.81, 0.0, 0.0, -9.81, 0.0, 0.0, -9.81, 0.0, 0.0, -9.81, 0.0, 0.0, -9.81, 0.0, 0.0, -9.81, 0.0, 0.0, -9.81, 0.0, 0.0, -9.81, 0.0, 0.0, -9.81, 0.0, 0.0, -9.81])
end
time = 0.1
res1 = zeros(SVector{6, KVec3})
res2 = deepcopy(res1)
res = reduce(vcat, vcat(res1, res2))
# call first time to compile
residual!(res, yd0, y0, kps3, time)
# measure allocations
@allocated residual!(res, yd0, y0, kps3, time)
end

test_residual()
``````

I had to rename `pos1, vel1, posd1, veld1` to `pos1_, vel1_, posd1_, veld1_` to avoid any memory allocations.

Still not clear to me: Why these 4 variables and not any othersβ¦

1 Like

Because these four variables are captured by closures which are created by the generator expressions.

To illustrate:

``````julia> function gengen()
a = if true; 1 else 1 end
if true; b=1 else b=1 end
return (a+x for x in 1:5), (b+x for x in 1:5)
end
gengen (generic function with 1 method)

julia> g1, g2 = gengen()
g1.f.a, g2.f.b
(1, Core.Box(1))

julia> g1..., g2...
(2, 3, 4, 5, 6, 2, 3, 4, 5, 6)

julia> g2.f.b.contents = 11
g1..., g2...
(2, 3, 4, 5, 6, 12, 13, 14, 15, 16)
``````

The rule is currently very (too) simple: if a local variable identifier is syntactically assigned more than once, or if itβs assigned in a conditional, and if it is captured by a closure, then it is boxed. It leads to a lot of unnecessary boxing. And boxes are currently very (too) simple: they expressly lose type information. It leads to a lot of unnecessarily slow code, and is a bug that must be fixed before Julia can be considered a serious language imo.

1 Like