Passing a struct to a function allocates whereas passing an array does not

Hi,

I noticed that the simple code example_1! below allocates memory (lots of it when this is embedded in a large loop) whereas example_2! does not.

  1. example_1! takes the 3-dimenional array conn[iel, :, :] and, within a look, assigns its value to the scalar m.

  2. example_2! takes the same array except that that array is an argument from a previously defined struct argument (mesh.conn[iel, :,:]) and is also assigned to the scalar m.

I can’t understand why example_1! allocates 2 orders of magnitude less than example_2!.

function example_1!(conn, iel)
    
    for l = 1:ngl,  i = 1:mesh.ngl

                m = conn[iel, i, l]
    end

end

40.873 μs (400 allocations: 6.25 KiB)

whereas:

function example_2!(st_mesh, iel)
    
    for l = 1:ngl, i = 1:mesh.ngl
    
                m = st_mesh.conn[iel, i, l]
    end

end

1.348 ms (41825 allocations: 653.52 KiB)

The minimal version of the struct is:

Base.@kwdef mutable struct St_mesh{TInt, TFloat}
       conn  = Array{Int64}(undef, 0)

       ngl::Union{TInt, Missing} = 4 #this is used defined and is changed later
end

where conn is allocated with the call:
mesh.conn = Array{Int64}(undef, mesh.nelem, mesh.ngl, mesh.ngl)
where
mesh = St_mesh{TInt,TFloat}(ngl)

When you passed an array directly in example_1!, it knew exactly what conn was because it knew it was passed an Array{Int64,3} so it didn’t have this problem. In example_2!, it knew that it got a St_mesh, but it couldn’t know (from the type St_mesh{TInt,TFloat}) what the .conn field was. This can be seen from

julia> fieldtypes(St_mesh{Int64,Float32})
(Any, Union{Missing, Int64})

Notice that the first field of your struct is not concretely typed (it’s Any). This means that any time the program looks at st_mesh.conn, it has no idea what it’s going to find. The surrounding code must be compiled to be ultra-generic. This causes it to be slow and allocating.

Try changing the definition of conn in your St_mesh struct definition to something concrete like conn::Array{Int64,3} = zeros(Int64,0,0,0).

1 Like

@mikmoore indeed that solved it. Thank you.