kongdd
February 13, 2025, 8:04am
1
using StructArrays
using BenchmarkTools
abstract type AbstractSoilParam{T} end
@kwdef mutable struct ParamCampbell{T} <: AbstractSoilParam{T}
θ_sat::T = 0.287 # [m3 m-3]
# θ_res::T = 0.075 # [m3 m-3]
ψ_sat::T = -10.0 # [cm]
Ksat::T = 34 / 3600 # [cm s-1]
b::T = 4.0 # [-]
end
mutable struct SoilParam{T}
par::StructVector{ParamCampbell{T}} # multiple layers
end
begin
N = 10
θ_sat = fill(0.287, N) # [m3 m-3]
ψ_sat = fill(-10.0, N) # [cm]
Ksat = fill(34 / 3600, N) # [cm s-1]
b = fill(4.0, N)
par = StructVector{ParamCampbell{Float64}}((θ_sat, ψ_sat, Ksat, b))
param = SoilParam{Float64}(par)
end
function test_param(param::SoilParam{Float64}, N::Int)
@inbounds for k in 1:10_000
for i in 1:N
_par = param.par[i]
end
end
end
julia> @btime test_param($param, N)
2.558 ms (100000 allocations: 4.58 MiB)
Any idea how to fix this?
If you’re going to use StructVector
you don’t need ParamCampbell
to be mutable struct
. When you index the vector, it creates a struct which allocates, as Julia cannot yet get rid of it.
Simply defining it as struct
should fix the allocations.
kongdd
February 13, 2025, 8:19am
3
Just tried. Remove mutable
not work.
This one works:
function solution(par::StructVector{ParamCampbell{T}}, N::Int) where {T<:Real}
@inbounds for k in 1:10_000
for i in 1:N
_par = par[i]
end
end
end
@btime solution($par, N)
# 3.200 ns (0 allocations: 0 bytes)
But have no idea why test_param
not.
It seems that, in this case, Julia simply compiles it to an empty function, thus not allocating. I’m not sure why indexing causes allocations.
Mason
February 13, 2025, 10:12am
5
The problem is that StructVector{ParamCampbell{T}}
is not a concrete type, since StructVector
needs more type parameters to be fully specified.
You could write
struct SoilParam{T, SV <: StructVector{ParamCampbell{T}}}
par::SV
end
instead.
4 Likes