I want to broadcast a vector-valued function and store the results in a higher dimensional array as opposed to a vector of arrays. I like the performance of this code:
import .Base: Broadcast
import .Broadcast: Broadcasted
function bccopyto!(dest::AbstractArray, bc::Base.Broadcast.Broadcasted)
Base.tail(axes(dest)) == axes(bc) || throw(ArgumentError("array could not" *
" be broadcast to be compatible with destination"))
bc′ = Base.Broadcast.preprocess(dest, bc)
@simd for I in eachindex(bc′)
@inbounds dest[:,I] = bc′[I]
end
return dest
end
@inline function materialize2array!(dest, bc::Broadcast.Broadcasted{Style}) where {Style}
return bccopyto!(dest, Broadcast.instantiate(Broadcast.Broadcasted{Style}(bc.f, bc.args, Base.tail(axes(dest)))))
end
broadcast2array!(f::Tf, dest, As::Vararg{Any,N}) where {Tf,N} = (materialize2array!(dest, Broadcast.broadcasted(f, As...),); dest)
A = Array{Float64,2}(undef,(2,100))
broadcast2array!(x -> [sin(x),cos(x)], A, 1:100) # 100 allocations: 9.38 KiB
The downside is that I cannot use the convenient dot-notation for broadcasting.
The alternatives that I have tried are hcat(broadcast(x->[sin(x),cos(x)], 1:100)...))
(106 allocations: 12.92 KiB) and creating a new type and interface with broadcast
:
import .Base: copyto!
mutable struct MyType{T,N} <: AbstractArray{T,N}
arr::Array{T,N}
end
Base.size(A::MyType) = size(A.arr)
Base.getindex(A::MyType, inds::Vararg{Int,N}) where {T,N} = A.arr[inds...]
Base.setindex!(A::MyType, val, inds::Vararg{Int,N}) where {T,N} = A.arr[inds...] = val
Base.showarg(io::IO, A::MyType, toplevel) = print(io, typeof(A))
function copyto!(dest::MyType, bc::Base.Broadcast.Broadcasted)
Base.tail(axes(dest)) == axes(bc) || throw(ArgumentError("array could not" *
" be broadcast to be compatible with destination"))
bc′ = Base.Broadcast.preprocess(dest, bc)
@simd for I in eachindex(bc′)
@inbounds dest[:,I] = bc′[I]
end
return dest
end
@inline function Broadcast.materialize!(dest::MyType, bc::Broadcast.Broadcasted{Style}) where {Style}
return copyto!(dest, Broadcast.instantiate(Broadcast.Broadcasted{Style}(bc.f, bc.args, Base.tail(axes(dest)))))
end
A = MyType(Array{Float64,2}(undef,(2,100))
broadcast!(x -> [sin(x), cos(x)], A, 1:100) # (300 allocations: 12.50 KiB)
Is there a better way to solve this problem?