Type Instability with AbstractArray

Consider the following code:

using Random

mutable struct mystate{Tq}
    x::AbstractArray{Tq,1}
end


function update!(state::mystate{Tq}) where Tq
    @. state.x[1] = 0.5 * state.x[2] + randn();
    @. state.x[2] = -0.5 * state.x[1] + randn();
    state
end

function prep(x0::AbstractArray{Tq,1}) where Tq

    state = mystate(x0);
    return state
    # return ar1(xdata, 100);
end

function integrate_path(x0::AbstractArray{Tq,1}, n_iters::Int) where Tq

    state= prep(x0);
    for j in 1:n_iters
        update!(state)
    end
    return state.x
end

X0 = [[1.],[-2.]];

Random.seed!(100);
# ar1(x0,100)
@code_warntype integrate_path(X0, 100)

On my v1.4 installation, this generates a type instability:

Variables
  #self#::Core.Compiler.Const(integrate_path, false)
  x0::Array{Array{Float64,1},1}
  n_iters::Int64
  state::mystate{Array{Float64,1}}
  @_5::Union{Nothing, Tuple{Int64,Int64}}
  j::Int64

Body::AbstractArray{Array{Float64,1},1}
1 ─       (state = Main.prep(x0))
β”‚   %2  = (1:n_iters)::Core.Compiler.PartialStruct(UnitRange{Int64}, Any[Core.Compiler.Const(1, false), Int64])
β”‚         (@_5 = Base.iterate(%2))
β”‚   %4  = (@_5 === nothing)::Bool
β”‚   %5  = Base.not_int(%4)::Bool
└──       goto #4 if not %5
2 β”„ %7  = @_5::Tuple{Int64,Int64}::Tuple{Int64,Int64}
β”‚         (j = Core.getfield(%7, 1))
β”‚   %9  = Core.getfield(%7, 2)::Int64
β”‚         Main.update!(state)
β”‚         (@_5 = Base.iterate(%2, %9))
β”‚   %12 = (@_5 === nothing)::Bool
β”‚   %13 = Base.not_int(%12)::Bool
└──       goto #4 if not %13
3 ─       goto #2
4 β”„ %16 = Base.getproperty(state, :x)::AbstractArray{Array{Float64,1},1}
└──       return %16

where, in my terminal, the AbstractArray{Array{Float64,1},1} appear in red (which I am interpreting as bad). If I switch to Array{Tq,1}, the instability disappears. Questions:

  1. Is this red actually bad from either a style/perfomance point of view?
  2. What am I losing by just using Array{Tq,1} instead? The reason I had wanted it to be more general was so that the actual code I am using could handle things like SharedArrays.
  3. Is there a way to avoid this type instability but maintain the flexibility of AbstractArray?
  4. Any other recommendations?

Don’t use abstract types for struct fields, as explained in the manual. If you want to support any type of AbstractArray here, then you can do:

mutable struct mystate{Tq,Tv<:AbstractVector{Tq}}
    x::Tv
end

or simply

mutable struct mystate{Tv<:AbstractVector}
    x::Tv
end

(AbstractVector{T} is just a convenient synonym for AbstractArray{T,1}.)

3 Likes

Also, there is no obvious reason for you to use a mutable struct in your particular example. As long as the field that you want to modify is mutable, the parent struct does not have to be mutable.

Furthermore, there is a strong convention to use CamelCase for type names, that is, MyState instead of mystate.

2 Likes