Hi!
I need to construct a struct that has a vector with elements of another structure as in this code:
mutable struct A{T}
value::T
end
mutable struct B
v::Vector{A}
end
get_data(a::A{T}) where T = a.value
get_data(b::B) = get_data.(b.v)
a1 = A(1)
a2 = A(true)
a3 = A(1.0)
b = B([a1,a2,a3])
Of course, get_data
in elements from A
are type stable:
julia> @code_warntype get_data(a1)
Variables
#self#::Core.Compiler.Const(get_data, false)
a::A{Int64}
Body::Int64
1 β %1 = ^[[A^[[DBase.getproperty(a, :value)::Int64
βββ return %1
julia> @code_warntype get_data(a2)
Variables
#self#::Core.Compiler.Const(get_data, false)
a::A{Bool}
Body::Bool
1 β %1 = Base.getproperty(a, :value)::Bool
βββ return %1
julia> @code_warntype get_data(a3)
Variables
#self#::Core.Compiler.Const(get_data, false)
a::A{Float64}
Body::Float64
1 β %1 = Base.getproperty(a, :value)::Float64
βββ return %1
However, get_data
in elements from B
is not:
julia> @code_warntype get_data(b)
Variables
#self#::Core.Compiler.Const(get_data, false)
b::B
Body::Union{BitArray{1}, Array}
1 β %1 = Base.getproperty(b, :v)::Array{A,1}
β %2 = Base.broadcasted(Main.get_data, %1)::Base.Broadcast.Broadcasted{Base.Broadcast.DefaultArrayStyle{1},Nothing,typeof(get_data),Tuple{Array{A,1}}}
β %3 = Base.materialize(%2)::Union{BitArray{1}, Array}
βββ return %3
This is leading to a huge performance hit in my original code. Can anyone help me to avoid this type-instability?
But the vector v
in B
can have an arbitrary number of A
s with different types. Like v = [A{Int}, A{Bool}, A{Float64}]
. Thus, I have no idea how to do this.
How many different types can hold A
?
Well, infinite. Because it can also hold user-defined structures.
1 Like
What about using map(get_data,b.v)
? as far as I know, map creates an array and widens the array type to hold the elements on demand, resorting to Any
as a fallback
I still get the type instability, even if all the elements in v
are of the same type:
julia> @code_warntype get_data(b)
Variables
#self#::Core.Compiler.Const(get_data, false)
b::B
Body::Array{_A,1} where _A
1 β %1 = Base.getproperty(b, :v)::Array{A,1}
β %2 = Main.map(Main.get_data, %1)::Array{_A,1} where _A
βββ return %2
Not if B
has A
as parameter:
julia> b
B{A{Int64}}(A{Int64}[A{Int64}(1), A{Int64}(2), A{Int64}(3)])
julia> @code_warntype get_data(b)
Variables
#self#::Core.Compiler.Const(get_data, false)
b::B{A{Int64}}
Body::Array{Int64,1}
1 β %1 = Base.getproperty(b, :v)::Array{A{Int64},1}
β %2 = Base.broadcasted(Main.get_data, %1)::Base.Broadcast.Broadcasted{Base.Broadcast.DefaultArrayStyle{1},Nothing,typeof(get_data),Tuple{Array{A{Int64},1}}}
β %3 = Base.materialize(%2)::Array{Int64,1}
βββ return %3
Also, it probably doesnβt affect type-stability, but does B
need to be mutable?
But how can I do that when v
can have for example [A{Int64}, A{Bool}, A{Float64}]
?
In this example no, but in my original code yes.
1 Like
If you want the freedom of having inhomogeneous vectors with any types, then type-instabilities are probably unavoidable
3 Likes
OK thanks! I though if I knew the type of all the elements in the vector at compile type, then I could make some tuple or something to avoid the type instability.
After this discussion, it seems that it can be improved. We have a way to make this type stable by changing the v
to a Tuple
is two conditions:
- The number of elements in
v
is fixed; or
- The types of elements in
v
are equal.
Thus, I think we can improve by having something like Vararg
that can define a number of different types
I am not expert and perhaps this is impossible. However, I opened a issue in Github to discuss this further:
https://github.com/JuliaLang/julia/issues/34640
Using @kristoffer.carlsson with @giordano I could solve this problem:
mutable struct A{T}
value::T
end
mutable struct B{V <: Tuple}
v::V
end
get_data(a::A{T}) where T = a.value
get_data(b::B) = map(get_data,b.v)
a1 = A(1)
a2 = A(true)
a3 = A(1.0)
b = B((a1,a2,a3))
julia> @code_warntype get_data(b)
Variables
#self#::Core.Compiler.Const(get_data, false)
b::B{Tuple{A{Int64},A{Bool},A{Float64}}}
Body::Tuple{Int64,Bool,Float64}
1 β %1 = Base.getproperty(b, :v)::Tuple{A{Int64},A{Bool},A{Float64}}
β %2 = Main.map(Main.get_data, %1)::Tuple{Int64,Bool,Float64}
βββ return %2
Thanks for the help!
1 Like