Skip to content

Broadcasting not type stable (but list comprehensions & map are) #55691

Open
@kaandocal

Description

@kaandocal

The following code (thanks @ysfoo) shows that broadcasting can fail to be type stable, even when equivalent map calls/list comprehensions are:

using LinearAlgebra

function chol_func1(mats::Vector{<:Matrix})
    return [getproperty(cholesky(mat), :L) for mat in mats]    
end
    
function chol_func2(mats::Vector{<:Matrix})
    choleskies = cholesky.(mats)
    return getproperty.(choleskies, :L) 
end

function chol_func3(mats::Vector{<:Matrix})
    choleskies = cholesky.(mats)
    return map(getproperty, choleskies, :L) 
end

bmats = [ [ 1 0; 0 1 ], [ 2 0; 0 2 ] ]

@code_warntype chol_func1(bmats)
@code_warntype chol_func2(bmats)    # NOT TYPE STABLE
@code_warntype chol_func3(bmats)

Output:

MethodInstance for chol_func1(::Vector{Matrix{Int64}})
  from chol_func1(mats::Vector{<:Matrix})
Arguments
  #self#::Core.Const(chol_func1)
  mats::Vector{Matrix{Int64}}
Locals
  #36::var"#36#37"
Body::Vector{LowerTriangular{Float64, Matrix{Float64}}}
1 ─      (#36 = %new(Main.:(var"#36#37")))%2 = #36::Core.Const(var"#36#37"())%3 = Base.Generator(%2, mats)::Base.Generator{Vector{Matrix{Int64}}, var"#36#37"}%4 = Base.collect(%3)::Vector{LowerTriangular{Float64, Matrix{Float64}}}
└──      return %4

MethodInstance for chol_func2(::Vector{Matrix{Int64}})
  from chol_func2(mats::Vector{<:Matrix})
Arguments
  #self#::Core.Const(chol_func2)
  mats::Vector{Matrix{Int64}}
Locals
  choleskies::Vector{Cholesky{Float64, Matrix{Float64}}}
Body::AbstractVector
1%1 = Base.broadcasted(Main.cholesky, mats)::Base.Broadcast.Broadcasted{Base.Broadcast.DefaultArrayStyle{1}, Nothing, typeof(cholesky), Tuple{Vector{Matrix{Int64}}}}
│        (choleskies = Base.materialize(%1))
│   %3 = Base.broadcasted(Main.getproperty, choleskies, :L)::Base.Broadcast.Broadcasted{Base.Broadcast.DefaultArrayStyle{1}, Nothing, typeof(getproperty), Tuple{Vector{Cholesky{Float64, Matrix{Float64}}}, Base.RefValue{Symbol}}}
│   %4 = Base.materialize(%3)::AbstractVector
└──      return %4                               ######### NOT TYPE STABLE

MethodInstance for chol_func3(::Vector{Matrix{Int64}})
  from chol_func3(mats::Vector{<:Matrix})
Arguments
  #self#::Core.Const(chol_func3)
  mats::Vector{Matrix{Int64}}
Locals
  choleskies::Vector{Cholesky{Float64, Matrix{Float64}}}
Body::Union{}
1%1 = Base.broadcasted(Main.cholesky, mats)::Base.Broadcast.Broadcasted{Base.Broadcast.DefaultArrayStyle{1}, Nothing, typeof(cholesky), Tuple{Vector{Matrix{Int64}}}}
│        (choleskies = Base.materialize(%1))
│        Main.map(Main.getproperty, choleskies, :L)
└──      Core.Const(:(return %3))

Version info:

Julia Version 1.10.3
Commit 0b4590a5507 (2024-04-30 10:59 UTC)
Build Info:
  Official https://julialang.org/ release
Platform Info:
  OS: Linux (x86_64-linux-gnu)
  CPU: 72 × Intel(R) Xeon(R) Gold 6254 CPU @ 3.10GHz
  WORD_SIZE: 64
  LIBM: libopenlibm
  LLVM: libLLVM-15.0.7 (ORCJIT, cascadelake)
Threads: 8 default, 0 interactive, 4 GC (on 72 virtual cores)
Environment:
  LD_LIBRARY_PATH = (...)
  JULIA_NUM_THREADS = 8

Changing :L to Ref(:L) doesn't make a difference for the first two examples. For the third (with map), using Ref(:L) loses type stability:

MethodInstance for chol_func3_ref(::Vector{Matrix{Int64}})
  from chol_func3(mats::Vector{<:Matrix})
Arguments
  #self#::Core.Const(chol_func3)
  mats::Vector{Matrix{Int64}}
Locals
  choleskies::Vector{Cholesky{Float64, Matrix{Float64}}}
Body::Vector
1%1 = Base.broadcasted(Main.cholesky, mats)::Base.Broadcast.Broadcasted{Base.Broadcast.DefaultArrayStyle{1}, Nothing, typeof(cholesky), Tuple{Vector{Matrix{Int64}}}}
│        (choleskies = Base.materialize(%1))
│   %3 = Main.getproperty::Core.Const(getproperty)
│   %4 = choleskies::Vector{Cholesky{Float64, Matrix{Float64}}}%5 = Main.Ref(:L)::Base.RefValue{Symbol}%6 = Main.map(%3, %4, %5)::Vector
└──      return %6                         ### NOT TYPE STABLE

Metadata

Metadata

Assignees

No one assigned

    Labels

    broadcastApplying a function over a collectionperformanceMust go faster

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions