Aliasing structs breaks type stability

Hi. I’ve defined some parametric structs like those defined in the Performance Tips section of the Julia manual. Since some of the structs had quite long names, I decided to alias them.

I was struggling to wrap my head around why @btime was so slow and why @code_warntype was giving me errors when I had the thought to try using the longer names for the structs. Lo and behold, type-stability was achieved.

Is this correct Julia behavior, and if so, is it documented anywhere? Or have I just not yet read the manual enough?

struct LongStructName{T<:Integer}
	i::T
end

ShStruct = LongStructName

# Check that they are totally equal
julia> ShStruct === LongStructName
true

# Use constructors in separate function
function create_objects(a::Integer, b::Integer)
	return LongStructName(a), LongStructName(b)
end
	
function create_objects_alias(a::Integer, b::Integer)
	return ShStruct(a), ShStruct(b)
end

julia> @code_warntype create_objects(1, 2)
Variables
  #self#::Core.Compiler.Const(create_objects, false)
  a::Int64
  b::Int64

Body::Tuple{LongStructName{Int64},LongStructName{Int64}}
1 ─ %1 = Main.LongStructName(a)::LongStructName{Int64}
│   %2 = Main.LongStructName(b)::LongStructName{Int64}
│   %3 = Core.tuple(%1, %2)::Tuple{LongStructName{Int64},LongStructName{Int64}}
└──      return %3


# Aliased name breaks type-stability
julia> @code_warntype create_objects_alias(1, 2)
Variables
  #self#::Core.Compiler.Const(create_objects_alias, false)
  a::Int64
  b::Int64

Body::Tuple{Any,Any}
1 ─ %1 = Main.ShStruct(a)::Any
│   %2 = Main.ShStruct(b)::Any
│   %3 = Core.tuple(%1, %2)::Tuple{Any,Any}
└──      return %

# Raw constructors do not give errors
julia> @code_warntype ShStruct(1)
Variables
  #self#::Type{LongStructName}
  i::Int64

Body::LongStructName{Int64}
1 ─ %1 = Core.apply_type(Main.LongStructName, $(Expr(:static_parameter, 1)))::Core.Compiler.Const(LongStructName{Int64}, false)
│   %2 = (%1)(i)::LongStructName{Int64}
└──      return %2

julia> @code_warntype ShStruct(1)
Variables
  #self#::Type{LongStructName}
  i::Int64

Body::LongStructName{Int64}
1 ─ %1 = Core.apply_type(Main.LongStructName, $(Expr(:static_parameter, 1)))::Core.Compiler.Const(LongStructName{Int64}, false)
│   %2 = (%1)(i)::LongStructName{Int64}
└──      return %2

You need to make the short name a const.

const ShStruct = LongStructName

This is the first performance tip: https://docs.julialang.org/en/v1/manual/performance-tips/index.html#Avoid-global-variables-1.

5 Likes

Oh. Oops. Never connected “structs” to “global variables” before. Thanks for the info!

2 Likes