Stream: helpdesk (published)

Topic: "Broadcast foreach"?


view this post on Zulip Philipp Gabler (Nov 21 2021 at 11:06):

Is there an established pattern to "broadcast" a function, but without allocation of output? Just for the sake of side-effects. I.e., map is to broadcast as foreach is to what?

I came up with this:

julia> function broadcast_foreach(op, args...)
           bc = Base.broadcasted(op, args...)
           foreach(x -> nothing, bc)
           return nothing
       end
broadcast_foreach (generic function with 1 method)

julia> xs = []
Any[]

julia> broadcast_foreach((xs, vs...) -> push!(xs, vs), Ref(xs), [2,3], [2,3])

julia> xs
2-element Vector{Any}:
 (2, 2)
 (3, 3)

julia> xs = []
Any[]

julia> broadcast_foreach((xs, vs...) -> push!(xs, vs), Ref(xs), [2,3], 1)

julia> xs
2-element Vector{Any}:
 (2, 1)
 (3, 1)

but not knowing as much about broadcasting machinery as I'd like to, there might be something existing I am overlooking.

view this post on Zulip Mason Protter (Nov 21 2021 at 18:57):

One approach you could take is making a struct Null such that writing data to it just throws away the data. Then you could do something like
Null() .= f.(x) and broadcast Fusion should take care of everything.

view this post on Zulip Philipp Gabler (Nov 22 2021 at 13:44):

I was thinking about that, too, but didn't know how to proceed. Some issues with broadcast style (I guess?):

julia> struct Null end

julia> Base.copyto!(::Null, bc::Broadcast.Broadcasted{Nothing}) = (foreach(_ -> nothing, bc); Null())

julia> f(xs, vs...) = push!(xs, vs)
f (generic function with 1 method)

julia> xs = []
Any[]

julia> Null() .= f.(Ref(xs), [2,3], 1)
ERROR: MethodError: no method matching ndims(::Type{Null})
...

view this post on Zulip Michael Abbott (Nov 22 2021 at 18:35):

I think you want to catch it earlier:

Broadcast.materialize!(dest::Null, bc::Base.Broadcast.Broadcasted) = (foreach(_ -> nothing, bc); Null())

view this post on Zulip Michael Abbott (Nov 22 2021 at 18:44):

It is not quite as efficient as ordinary broadcasting. IIRC there were some such issues with sum(::Broadcasted):

julia> x = rand(100, 100); y = rand(100, 100);

julia> @btime sin.($x ./ $y .+ 1);
  min 77.958 μs, mean 86.330 μs (2 allocations, 78.17 KiB. GC mean 2.82%)

julia> @btime nothing .= sin.($x ./ $y .+ 1);
  min 150.667 μs, mean 154.272 μs (0 allocations)

view this post on Zulip Mason Protter (Nov 23 2021 at 19:08):

Hm. I would have thought that Broadcast.instantiate(bc) would have fixed it but it didn't


Last updated: Nov 22 2024 at 04:41 UTC