Hi, I am trying to overload getproperty
for a struct I have.
I do:
function Base.getproperty(m::ABM, s::Symbol)
if s β (:agents, :space, :scheduler, :properties)
return Base.getfield(m, s)
else
return Base.getindex(m.properties, s)
end
end
So, my end goal is to still be able to write model.agents
and get the actual field of the struct, but if I type model.s
to get the value of [:s]
from the underlying dictionary. The method as I have written it above works just fine and does what it should.
Now, before adding this method, I do:
julia> @btime $(model).properties[:Ο΅]
9.000 ns (0 allocations: 0 bytes)
0.4
julia> @btime $(model).scheduler
0.001 ns (0 allocations: 0 bytes)
fastest (generic function with 1 method)
julia> @code_warntype model.properties[:Ο΅]
Variables
#self#::Core.Compiler.Const(getindex, false)
h::Dict{Symbol,Float64}
key::Symbol
val::Union{}
index::Int64
Body::Float64
1 β Core.NewvarNode(:(val))
β (index = Base.ht_keyindex(h, key))
β $(Expr(:inbounds, true))
β %4 = (index < 0)::Bool
βββ goto #3 if not %4
2 β %6 = Base.KeyError(key)::KeyError
β Base.throw(%6)
βββ Core.Compiler.Const(:(return %7), false)
3 β %9 = Base.getproperty(h, :vals)::Array{Float64,1}
β %10 = Base.getindex(%9, index)::Float64
β %11 = Core.typeassert(%10, $(Expr(:static_parameter, 2)))::Float64
βββ return %11
4 β $(Expr(:inbounds, :pop))
βββ Core.Compiler.Const(:(return val), false)
I then add the method, and I have:
julia> @btime $(model).scheduler
17.735 ns (0 allocations: 0 bytes)
fastest (generic function with 1 method)
julia> @btime $(model).properties[:Ο΅]
49.545 ns (1 allocation: 16 bytes)
0.4
julia> @btime $(model).Ο΅
49.848 ns (1 allocation: 16 bytes)
0.4
julia> @code_warntype model.Ο΅
Variables
#self#::Core.Compiler.Const(getproperty, false)
m::AgentBasedModel{HKAgent,Nothing,typeof(fastest),Dict{Symbol,Float64}}
s::Symbol
Body::Any
1 β %1 = (:agents, :space, :scheduler, :properties)::Core.Compiler.Const((:agents, :space, :scheduler, :properties), false)
β %2 = (s β %1)::Bool
βββ goto #3 if not %2
2 β %4 = Base.getfield::Core.Compiler.Const(getfield, false)
β %5 = (%4)(m, s)::Union{typeof(fastest), Nothing, Dict}
βββ return %5
3 β %7 = Base.getindex::Core.Compiler.Const(getindex, false)
β %8 = Base.getproperty(m, :properties)::Any
β %9 = (%7)(%8, s)::Any
βββ return %9
Performance dropped and I have type instability. Is there a way to achieve what I want, or is it strictly impossible to use getproperty
to do both getfield
as well as something else?