Help to remove type instability when using getproperty

Hi!

I have this structure:

struct Workspace{T}
    x::Vector{T}
    s_i::Vector{T}
    s_b::Vector{T}
end

I would like to overload getproperty and getindex so that s.JD is x[1] and s.w is view(x,2:4). Following the same approach as LabelledArrays, I did the following:

mutable struct Workspace{T}
    x::Vector{T}
    s_i::Vector{T}
    s_b::Vector{T}
end

# Functions to translate the state-vector into variables.
function Base.getproperty(a::Workspace, f::Symbol)
    if (f ∈ [:JD, :w])
        return getindex(a, Val(f))
    else
        return getfield(a,f)
    end
end

function Base.getindex(a::Workspace, s::Val)
    return __getindex(a,s)
end

function __getindex(a::Workspace, ::Val{:JD})
    return a.x[1]
end

function __getindex(a::Workspace, ::Val{:w})
    return view(a.x, 2:4)
end

This works fine and I can access those virtual fields without type-instabilities:

julia> w = Workspace(zeros(10), zeros(3), zeros(3))
Workspace{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])

julia> function test(w)
       w.JD
       end
test (generic function with 1 method)

julia> @code_warntype test(w)
Variables
  #self#::Core.Compiler.Const(test, false)
  w::Workspace{Float64}

Body::Float64
1 ─ %1 = Base.getproperty(w, :JD)::Float64
└──      return %1

However, if a have another type of variable, like a Bool, then the same code has a type instability:

mutable struct Workspace{T}
    x::Vector{T}
    eclipse::Bool
    s_i::Vector{T}
    s_b::Vector{T}
end

# Functions to translate the state-vector into variables.
function Base.getproperty(a::Workspace, f::Symbol)
    if (f ∈ [:JD, :w])
        return getindex(a, Val(f))
    else
        return getfield(a,f)
    end
end

function Base.getindex(a::Workspace, s::Val)
    return __getindex(a,s)
end

function __getindex(a::Workspace, ::Val{:JD})
    return a.x[1]
end

function __getindex(a::Workspace, ::Val{:w})
    return view(a.x, 2:4)
end
julia> w = Workspace(zeros(10), false, zeros(3), zeros(3))
Workspace{Float64}([0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], false, [0.0, 0.0, 0.0], [0.0, 0.0, 0.0])

julia> function test(w)
       w.JD
       end
test (generic function with 1 method)

julia> @code_warntype test(w)
Variables
  #self#::Core.Compiler.Const(test, false)
  w::Workspace{Float64}

Body::Any
1 ─ %1 = Base.getproperty(w, :JD)::Any
└──      return %1

Is there any way to avoid that?

Here’s a blog post on how LabelledArrays was made. If you copy that strategy you should be fine:

2 Likes

Hum, it seems that I really used the same approach (at least I was not able to understand the difference). As you put on the blog, it works fine if all the variables in the structure Workspace have the same type. If one variable has another type, then I was not able to avoid the type-instability. Of course, my example is way simpler than LabelledArrays because the β€œlabels” are fixed and known.

I think the same problem happens with LabelledArrays (well, not exactly the same problem) if the element types of the array are different, leading to a Array{Any}.

julia> x = @LArray [1,2,3,"s"] (a = 1:3, b = 4);

julia> h(x) = x.b
h (generic function with 1 method)

julia> @code_warntype h(x)
Variables
  #self#::Core.Compiler.Const(h, false)
  x::LArray{Any,1,Array{Any,1},(a = 1:3, b = 4)}

Body::Any
1 ─ %1 = Base.getproperty(x, :b)::Any
└──      return %1

You can’t have an array as the backing if you want to put different types into it.

1 Like

I know, but it seems that I even cannot have different types into the other structure fields. Notice that my array x inside Workspace is Vector{Float64}. Can it be a bug in Julia?

I found a way!

mutable struct Workspace{T}
    x::Vector{T}
    eclipse::Bool
    s_i::Vector{T}
    s_b::Vector{T}
end

# Functions to translate the state-vector into variables.
@inline Base.getproperty(a::Workspace, f::Symbol) = getindex(a, Val(f))
@inline Base.getindex(a::Workspace, s::Val) = __getindex(a,s)
@inline __getindex(a::Workspace, ::Val{:JD}) = getfield(a,:x)[1]
@inline __getindex(a::Workspace, ::Val{:w}) = view(getfield(a,:x), 2:4)
@inline __getindex(a::Workspace, ::Val{F}) where F = getfield(a,F)

No type instabilities :slight_smile: Thanks @ChrisRackauckas

2 Likes