Ok this seems to be working fine (not as fast as writing a for loop but a “good” compromise):
function set_awfb!(func::F, a, w, b::Vararg{T, N}) where {F, T, N}
for i in w
@inbounds a[i] = func(map(x -> x[i], b)...)
end
end
which is a generic version of the function suggested by @lmiq and I used splatting with map
(suggestion found here: Work around splatting to avoid unecessary allocations) and type declaration (suggestion found here: Splatting arguments causes ~30x slow down) to avoid allocations.
An idea of the performance with a simple addition:
N = 10000;
a = ones(N); b = rand(N); c = rand(N);
w = collect(1:N)[rand(N) .< 0.5];
@btime set_awfb!(+, $a, $w, $b, $c)
6.540 μs (0 allocations: 0 bytes)
@btime for i in $w
@inbounds $a[i] = $b[i] + $c[i]
end
5.402 μs (0 allocations: 0 bytes)
@btime $a[$w] .= $b[$w] .+ $c[$w];
17.878 μs (4 allocations: 78.03 KiB)
So still slower, but a good improvement compared to broadcasting!