Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Struct containing a StructArray is not type stable #125

Closed
robsmith11 opened this issue Apr 20, 2020 · 4 comments
Closed

Struct containing a StructArray is not type stable #125

robsmith11 opened this issue Apr 20, 2020 · 4 comments

Comments

@robsmith11
Copy link

robsmith11 commented Apr 20, 2020

After converting my project to use StructArrays, I was surprised to see that there are now millions of allocations. The culprit seems to be a type instability with StructArrays, which causes some functions like searchsorted to allocate:

julia> struct ST
       x::StructArray{typeof((a=0,))}
       end

julia> s = ST(StructArray{typeof((a=0,))}(undef, 0))
ST(NamedTuple{(:a,),Tuple{Int64}}[])

julia> @code_warntype s.x
Variables
  #self#::Core.Compiler.Const(getproperty, false)
  x::ST
  f::Symbol

Body::StructArray{NamedTuple{(:a,),Tuple{Int64}},N,C,I} where I where C<:Union{Tuple, NamedTuple} where N
1 ─ %1 = Base.getfield(x, f)::StructArray{NamedTuple{(:a,),Tuple{Int64}},N,C,I} where I where C<:Union{Tuple, NamedTuple} where N
└──      return %1

julia> @btime searchsorted($s.x.a, 0)
  111.993 ns (1 allocation: 32 bytes)
1:0

julia> @btime searchsorted($s.x.a::Vector{Int}, 0)
  63.711 ns (0 allocations: 0 bytes)
1:0

@robsmith11
Copy link
Author

I was able to fix the type stability by specifying the full very verbose type in the struct definition. Is there any more compact alternative?

julia> struct ST2
       x::StructArray{NamedTuple{(:a,),Tuple{Int64}},1,NamedTuple{(:a,),Tuple{Array{Int64,1}}},Int64}
       end

julia> s = ST2(StructArray{NamedTuple{(:a,),Tuple{Int64}}}(undef, 0))
ST2(NamedTuple{(:a,),Tuple{Int64}}[])

julia> @code_warntype s.x
Variables
  #self#::Core.Compiler.Const(getproperty, false)
  x::ST2
  f::Symbol

Body::StructArray{NamedTuple{(:a,),Tuple{Int64}},1,NamedTuple{(:a,),Tuple{Array{Int64,1}}},Int64}
1 ─ %1 = Base.getfield(x, f)::StructArray{NamedTuple{(:a,),Tuple{Int64}},1,NamedTuple{(:a,),Tuple{Array{Int64,1}}},Int64}
└──      return %1

julia> @btime searchsorted($s.x.a, 0)
  9.175 ns (0 allocations: 0 bytes)
1:0

@KristofferC
Copy link
Collaborator

I was able to fix the type stability by specifying the full very verbose type in the struct definition. Is there any more compact alternative?

You need your field to be concrete so this isn't very surprising. You can write it as

struct ST2{ST <: StructArray}
    x::ST
end

@robsmith11
Copy link
Author

Yeah I guess that makes sense. My mistake was incorrectly thinking that StructArray{Foo} was a concrete type.

@SteffenPL
Copy link

Maybe not relevant, but I came across this and found

typeof(StructArray(YourType[]))

as an easy way to get the concrete type.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants