Allocation-free weighted samples

Assuming the type (including length for an SVector) of the weights does not change, you could exploit the mutability of Weights to update it in-place:

using Accessors, StaticArrays, StatsBase, BenchmarkTools
StatsBase.weights(w::Weights) = w
function plusone(wghts, res)
    for i in 1:100
        # Update in any way compatible with typeof(wghts).
        # Here this is Weights{Float64, Float64, SVector{16, Float64}}.
        # (The parameters are the types of the sum, the entries, and the weights vector.)
        wghts.values = @SVector rand(length(wghts.values))  
        wghts.sum = sum(wghts.values) 
        idx = wsample(wghts)
        @reset res[idx] += 1
    end
   return res
end

wts = @SVector rand(16)
wghts = Weights(wts)  # In the example wts is not used directly, but fixes the type
res = @SVector fill(0, 16)
@btime plusone($wghts, $res);
    # 5.250 μs (0 allocations: 0 bytes)

To avoid forgetting to update wghts.sum, you could also use a function

function update!(w::Weights{S, T, V}, new_wts::V) where {S, T, V}
    w.values = new_wts
    w.sum = sum(w.values)
end
2 Likes