BroadcastArray
in LazyArrays.jl ( which is equivalent to Broadcasted
but <: AbstractArray
) gets rid of the type inference issue:
julia> g̃(v) = mapreduce(identity, +, BroadcastArray(*, v, v'))
g̃ (generic function with 1 method)
julia> @code_warntype g̃(v)
Body::Float64
1 1 ── %1 = %new(Adjoint{Float64,Array{Float64,1}}, v)::Adjoint{Float64,Array{Float64,1}}
│ %2 = (LazyArrays.tuple)(v, %1)::Tuple{Array{Float64,1},Adjoint{Float64,Array{Float64,1}}}
│ %3 = (Base.arraysize)(v, 1)::Int64 │││╻╷╷╷ instantiate
│ %4 = (Base.slt_int)(%3, 0)::Bool ││││╻╷╷╷ combine_axes
│ %5 = (Base.ifelse)(%4, 0, %3)::Int64│││││┃│││││ broadcast_axes
│ %6 = %new(Base.OneTo{Int64}, %5)::Base.OneTo{Int64} axes
│ (Base.ifelse)(false, 0, 1) ││││││╻╷╷╷╷ broadcast_axes
│ %8 = %new(Base.OneTo{Int64}, 1)::Base.OneTo{Int64} axes
│ %9 = (Base.arraysize)(v, 1)::Int64 ││││││││╻ axes
│ %10 = (Base.slt_int)(%9, 0)::Bool │││││││││╻╷╷╷ map
│ %11 = (Base.ifelse)(%10, 0, %9)::Int64│││││││││┃││ Type
│ %12 = %new(Base.OneTo{Int64}, %11)::Base.OneTo{Int64} Type
│ %13 = (1 === %5)::Bool ││││││╻╷╷╷╷ _bcs
│ %14 = (Base.and_int)(true, %13)::Bool│││││││╻ _bcs1
└─── goto #3 if not %14 ││││││││┃ _bcsm
2 ── goto #4 │││││││││
3 ── %17 = (%5 === 1)::Bool │││││││││╻ ==
└─── goto #4 │││││││││
4 ┄─ %19 = φ (#2 => %14, #3 => %17)::Bool ││││││││
└─── goto #6 if not %19 ││││││││
5 ── goto #12 ││││││││
6 ── %22 = (%5 === 1)::Bool │││││││││╻╷ ==
│ %23 = (Base.and_int)(true, %22)::Bool││││││││││╻ &
└─── goto #8 if not %23 │││││││││
7 ── goto #9 │││││││││
8 ── goto #9 │││││││││
9 ┄─ %27 = φ (#7 => %23, #8 => true)::Bool││││││││
└─── goto #11 if not %27 ││││││││
10 ─ goto #12 ││││││││
11 ─ %30 = %new(Base.DimensionMismatch, "arrays could not be broadcast to a common size")::DimensionMismatch
│ (Base.Broadcast.throw)(%30) ││││││││
└─── $(Expr(:unreachable)) ││││││││
12 ┄ %33 = φ (#5 => %8, #10 => %6)::Base.OneTo{Int64}
│ %34 = (Core.tuple)(%33, %12)::Tuple{Base.OneTo{Int64},Base.OneTo{Int64}}
└─── goto #13 │││││││
13 ─ goto #14 ││││││
14 ─ goto #15 │││││
15 ─ %38 = %new(Base.Broadcast.Broadcasted{Base.Broadcast.DefaultArrayStyle{2},Tuple{Base.OneTo{Int64},Base.OneTo{Int64}},typeof(*),Tuple{Array{Float64,1},Adjoint{Float64,Array{Float64,1}}}}, *, %2, %34)::Base.Broadcast.Broadcasted{Base.Broadcast.DefaultArrayStyle{2},Tuple{Base.OneTo{Int64},Base.OneTo{Int64}},typeof(*),Tuple{Array{Float64,1},Adjoint{Float64,Array{Float64,1}}}}
└─── goto #16 ││││
16 ─ %40 = %new(BroadcastArray{Float64,2,Base.Broadcast.Broadcasted{Base.Broadcast.DefaultArrayStyle{2},Tuple{Base.OneTo{Int64},Base.OneTo{Int64}},typeof(*),Tuple{Array{Float64,1},Adjoint{Float64,Array{Float64,1}}}}}, %38)::BroadcastArray{Float64,2,Base.Broadcast.Broadcasted{Base.Broadcast.DefaultArrayStyle{2},Tuple{Base.OneTo{Int64},Base.OneTo{Int64}},typeof(*),Tuple{Array{Float64,1},Adjoint{Float64,Array{Float64,1}}}}}
└─── goto #17 │││
17 ─ goto #18 ││
18 ─ %43 = Main.identity::Core.Compiler.Const(identity, false)
│ %44 = Main.:+::Core.Compiler.Const(+, false)
│ %45 = invoke Base.mapfoldl_impl(%43::Function, %44::Function, $(QuoteNode(NamedTuple()))::NamedTuple{(),Tuple{}}, %40::BroadcastArray{Float64,2,Base.Broadcast.Broadcasted{Base.Broadcast.DefaultArrayStyle{2},Tuple{Base.OneTo{Int64},Base.OneTo{Int64}},typeof(*),Tuple{Array{Float64,1},Adjoint{Float64,Array{Float64,1}}}}})::Float64
└─── return %45 │
Though speedwise it’s no better:
julia> @btime g($v)
14.375 μs (3 allocations: 64 bytes)
2690.6330912377566
julia> @btime g̃($v)
16.576 μs (12 allocations: 256 bytes)
2690.6330912377566