Consider the following simple example. I have overloaded getindex
and getproperty
for MyType
in the exact same way, but one allows constant propagation and the other doesn’t. I’m not even using x[:c]
and x.c
; I’m calling them both the same way.
using BenchmarkTools
struct Axis{IndexMap} end
Axis(;kwargs...) = Axis{(;kwargs...)}()
Base.getindex(ax::Axis, s::Symbol) = _getindex(ax, Val(s))
@generated _getindex(::Axis{IM}, ::Val{s}) where {IM,s} = :($(getfield(IM, s)))
struct MyType{T,N,A<:AbstractArray{T,N},Axes}
data::A
ax::Axes
end
Base.getindex(x::MyType, s::Symbol) = MyType(getfield(x, :data), getindex(getfield(x, :ax), s))
Base.getproperty(x::MyType, s::Symbol) = MyType(getfield(x, :data), getindex(getfield(x, :ax), s))
function test_index(x)
return getindex(x, :c)
end
function test_prop(x)
return getproperty(x, :c)
end
ax = Axis(a=1, b=2:4, c=(a=5:6, b=7))
mt = MyType(rand(7), ax)
@btime test_index($mt) # 4.600 μs (1 allocation: 16 bytes)
@btime test_prop($mt) # 5.701 ns (1 allocation: 16 bytes)
What’s strange is I can remove just the call to the MyType
constructor and things are fast again:
Base.getindex(x::MyType, s::Symbol) = (getfield(x, :data), getindex(getfield(x, :ax), s))
@btime test_index($mt) # 5.701 ns (1 allocation: 16 bytes)
Then I thought it might help to wrap s
as a Val
immediately and call _getindex
directly, but that didn’t help either:
Base.getindex(x::MyType, s::Symbol) = MyType(getfield(x, :data), _getindex(getfield(x, :ax), Val(s)))
@btime test_index($mt) # 4.628 μs (1 allocation: 16 bytes)
What’s going on here?