# Reducing allocations for repeatedly generating MvNormal distributions

I have to repeatedly generate few random numbers from many different bivariate normal distributions. I want to do this with as few allocations as possible. A simplified example of my code:

``````using Distributions
using Random, BenchmarkTools
function generate_measurement_model(x,θ)
Σ = [x[1]*θ[1] x[1]*θ[2]; x[1]*θ[2] x[1]*θ[1]]
μ = [x[1]^2 + x[2]^2; x[1]*θ[1]]
MvNormal(μ,Σ)
end
function measure!(y,x,θ)
measurement_model = generate_measurement_model(x,θ)
rand!(measurement_model,y)
return nothing
end
function main!(y_particles,x_particles,θ)
length_particle = size(y_particles,1)
num_particles = size(y_particles,2)
for k = 1:num_particles
measure!(@view(y_particles[:,k]),x_particles[:,k],θ)
end
return nothing
end
Random.seed!(123);
n = 100_000
y_particles = rand(2,n)
x_particles = rand(2,n)
θ = [1.0; 0.1]
main!(y_particles,x_particles,θ)
@btime main!(y_particles,x_particles,θ) #49ms 69MiB
``````

I have tried to get rid of as many allocations as possible, but did not get rid of everything.
Particularly cholesky seems to still allocate quite a bit, in my naive opinion. I would appreciate any advice.

``````using Distributions
using LinearAlgebra
using PDMats
using Random, BenchmarkTools
function generate_measurement_model(x,θ,μ,Σ,Σ_copy)
Σ[1,1] = x[1]*θ[1]
Σ[1,2] = x[1]*θ[2]
Σ[2,1] = x[1]*θ[2]
Σ[2,2] = x[1]*θ[1]
copy!(Σ_copy,Σ)
Σ_chol = cholesky!(Σ_copy)
Σ_PDM = PDMat(Σ,Σ_chol)
μ[1] = x[1]^2 + x[2]^2
μ[2] = x[1]*θ[1]
MvNormal(μ,Σ_PDM)
end
function measure!(y,x,θ)
measurement_model = generate_measurement_model(x,θ)
rand!(measurement_model,y)
return nothing
end
function main!(y_particles,x_particles,θ,μ,Σ,Σ_copy)
n = size(y_particles,2)
for k = 1:n
measure!(@view(y_particles[:,k]),@view(x_particles[:,k]),θ)
end
end
Random.seed!(123);
n = 100_000
y_particles = rand(2,n)
x_particles = rand(2,n)
θ = [1.0; 0.1]
μ = Vector{Float64}(undef,2)
Σ = Matrix{Float64}(undef,2,2)
Σ_copy = similar(Σ)
generate_measurement_model(x,θ) = generate_measurement_model(x,θ,μ::Vector{Float64},Σ::Matrix{Float64},Σ_copy::Matrix{Float64})
main!(y_particles,x_particles,θ,μ,Σ,Σ_copy)
@btime main!(y_particles,x_particles,θ,μ,Σ,Σ_copy)  #31ms 27MiB
@code_warntype main!(y_particles,x_particles,θ,μ,Σ,Σ_copy)
``````

I would normally suggest StaticArrays, but unfortunately `PDMat` does not accept an `SMatrix`. You could make a PR to PDMats.jl fixing this, and use static vectors throughout, that should be very fast.

Incidentally, don’t use `[a; b]` for vectors. You really want `[a, b]`.

1 Like

Thank you for the tips, I will give StaticArrays a shot.