Hi,
I am seeing some suspect allocations using broadcasting defined on a custom type, in particular when the broadcast involves Number
s. The MWE is reported below. My actual code is somewhat larger but this example reproduce the behaviour.
struct Foo{T, A<:AbstractMatrix{T}} <: AbstractMatrix{T}
data::A
end
Foo(data::A) where {A<:AbstractMatrix} = Foo{eltype(data), A}(data)
@inline Base.unsafe_get(f::Foo) = f.data
# Catch call to broadcast, then rebroadcast to field data
@generated function Base.Broadcast.broadcast!(f, dest::Foo, src::Vararg{Any, N}) where N
args = [:(unsafe_get(src[$k])) for k = 1:N]
quote
broadcast!(f, unsafe_get(dest), $(args...))
return dest
end
end
# function that allocates
function bar(out, c::Number, x)
for i = 1:10000
out .= x .* c
end
out
end
x = Foo(randn(100, 100))
out = Foo(randn(100, 100))
c = 1.0
@show @allocated bar(out, c, x)
@show @allocated bar(out, c, x)
@show @allocated bar(out, c, x)
@show @allocated bar(out, c, x)
The type Foo
is the type I want to do broadcasting on, e.g., in the function bar
. I have overloaded broadcast!
on my custom type using a generated function approach. The above code results in
@allocated(bar(out, c, x)) = 3955676
@allocated(bar(out, c, x)) = 160000
@allocated(bar(out, c, x)) = 160000
@allocated(bar(out, c, x)) = 160000
If you change the line in the loop in bar
to out .= x .* x
, all allocations disappear. Any pointers are welcome.
Thanks