Skip to content

DiagNormal is substantially slower than Product of Normals #1989

@penelopeysm

Description

@penelopeysm

"""
product_distribution(dists::AbstractVector{<:Normal})
Create a multivariate normal distribution by stacking the univariate normal distributions.
The resulting distribution of type [`MvNormal`](@ref) has a diagonal covariance matrix.
"""
function product_distribution(dists::AbstractVector{<:Normal})
µ = map(mean, dists)
σ2 = map(var, dists)
return MvNormal(µ, Diagonal(σ2))
end

product_distribution on an array of Normals creates a DiagNormal. That's actually quite satisfying to look at, but the original Product is quite substantially faster:

julia> using Distributions, Chairmarks

julia> dists = [Normal(0.0), Normal(1.0), Normal(2.0, 3.0), Normal(4.0, 0.2)];

julia> prod = Product(dists);

julia> mvnorm = product_distribution(dists);

rand

julia> @be rand($prod)
Benchmark: 3556 samples with 805 evaluations
 min    23.034 ns (2 allocs: 96 bytes)
 median 24.845 ns (2 allocs: 96 bytes)
 mean   33.334 ns (2 allocs: 96 bytes, 0.25% gc time)
 max    5.135 μs (2 allocs: 96 bytes, 98.75% gc time)

julia> @be rand($mvnorm)
Benchmark: 8044 samples with 260 evaluations
 min    32.212 ns (2 allocs: 96 bytes)
 median 34.935 ns (2 allocs: 96 bytes)
 mean   42.677 ns (2 allocs: 96 bytes, 0.07% gc time)
 max    13.362 μs (2 allocs: 96 bytes, 98.83% gc time)

logpdf

julia> x = rand(prod)
4-element Vector{Float64}:
  1.7947570646417756
  1.050115051070052
 -1.0885138050761758
  4.265595405348341

julia> @be logpdf($prod, $x)
Benchmark: 3290 samples with 1220 evaluations
 min    23.498 ns
 median 23.599 ns
 mean   23.693 ns
 max    44.125 ns

julia> @be logpdf($mvnorm, $x)
Benchmark: 2827 samples with 464 evaluations
 min    61.332 ns (2 allocs: 96 bytes)
 median 62.409 ns (2 allocs: 96 bytes)
 mean   71.665 ns (2 allocs: 96 bytes, 0.14% gc time)
 max    9.668 μs (2 allocs: 96 bytes, 98.51% gc time)

Similar performance differences are observed when taking the gradient of logpdf.

Perhaps there are some performance improvements that can be done for DiagNormal?

repro

(ppl) pkg> st
Status `~/ppl/Project.toml`
  [0ca39b1e] Chairmarks v1.3.1
  [31c24e10] Distributions v0.25.120

julia> versioninfo()
Julia Version 1.11.5
Commit 760b2e5b739 (2025-04-14 06:53 UTC)
Build Info:
  Official https://julialang.org/ release
Platform Info:
  OS: macOS (arm64-apple-darwin24.0.0)
  CPU: 10 × Apple M1 Pro
  WORD_SIZE: 64
  LLVM: libLLVM-16.0.6 (ORCJIT, apple-m1)
Threads: 1 default, 0 interactive, 1 GC (on 8 virtual cores)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions