# mul!(::Matrix, ::Matrix, ::UpperTriangular) is really slow

**URL:** <https://discourse.julialang.org/t/mul-matrix-matrix-uppertriangular-is-really-slow/69368>\
**Category:** Performance\
**Tags:** linearalgebra\
**Created:** [October 7, 2021, 12:50pm UTC](https://discourse.julialang.org/t/mul-matrix-matrix-uppertriangular-is-really-slow/69368 "2021-10-07T12:50:11Z")\
**Posts on this page:** 5\
**Page:** 1

<div class="post-metadata">

**Author:** ![Luapulu](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/luapulu/32/17669_2.png) [@Luapulu](https://discourse.julialang.org/u/Luapulu)\
**Post date:** [October 7, 2021, 12:50pm UTC](https://discourse.julialang.org/t/mul-matrix-matrix-uppertriangular-is-really-slow/69368/1 "2021-10-07T12:50:11Z")

</div>

There seem to be efficient methods to do `mul!(out, ::UpperTriangular, ::Matrix)` and `mul!(out, ::LowerTriangular, ::Matrix)`. However, when you reverse the arguments and use `mul!(out, ::Matrix, ::UpperTriangular)` the performance is much much worse! It’s much worse than just using `mul!(out, ::Matrix, ::Matrix)`.

Benchmarks

```julia
using LinearAlgebra
using BenchmarkTools

n = 1000

prealloc = Matrix{Float64}(undef, n, n)

@benchmark mul!($prealloc, m, v) setup=begin
    m = rand(n, n)
    v = rand(n, n)
end # 12 ms

@benchmark mul!($prealloc, m, v) setup=begin
    m = UpperTriangular(rand(n, n))
    v = rand(n, n)
end # 8 ms

@benchmark mul!($prealloc, m, v) setup=begin
    m = LowerTriangular(rand(n, n))
    v = rand(n, n)
end # 8 ms

@benchmark mul!($prealloc, m, v) setup=begin
    m = rand(n, n)
    v = UpperTriangular(rand(n, n))
end # 800 ms

@benchmark mul!($prealloc, m, v) setup=begin
    m = rand(n, n)
    v = LowerTriangular(rand(n, n))
end # 800 ms

```

This is a bug, right?

---

<div class="post-metadata">

**Author:** ![sostock](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/sostock/32/5546_2.png) [@sostock](https://discourse.julialang.org/u/sostock)\
**Post date:** [October 7, 2021, 1:03pm UTC](https://discourse.julialang.org/t/mul-matrix-matrix-uppertriangular-is-really-slow/69368/2 "2021-10-07T13:03:04Z")

</div>

The fast methods use BLAS calls while the slow ones (where `v` is the triangular matrix) fall back to the generic matmul algorithm. It shouldn’t be difficult to add methods to dispatch them to BLAS as well.

---

<div class="post-metadata">

**Author:** ![goerch](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/goerch/32/29122_2.png) [@goerch](https://discourse.julialang.org/u/goerch)\
**Post date:** [October 7, 2021, 1:16pm UTC](https://discourse.julialang.org/t/mul-matrix-matrix-uppertriangular-is-really-slow/69368/3 "2021-10-07T13:16:47Z")

</div>

Maybe this helps in the meantime:

```julia
@btime mul!($prealloc, m', v')' setup=begin
    m = UpperTriangular(rand(n, n))
    v = rand(n, n)
end  

@btime mul!($prealloc, m', v')' setup=begin
    m = LowerTriangular(rand(n, n))
    v = rand(n, n)
end  

```

---

<div class="post-metadata">

**Author:** ![Luapulu](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/luapulu/32/17669_2.png) [@Luapulu](https://discourse.julialang.org/u/Luapulu)\
**Post date:** [October 7, 2021, 1:18pm UTC](https://discourse.julialang.org/t/mul-matrix-matrix-uppertriangular-is-really-slow/69368/4 "2021-10-07T13:18:41Z")

</div>

I thought about that as well, but then I’m no longer returning `prealloc`, but the adjoint of `prealloc`, which isn’t so clean. I’d prefer to pay the slight performance cost and just not annotate my matrix with `UpperTriangular` until this gets fixed.

---

<div class="post-metadata">

**Author:** ![sostock](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/sostock/32/5546_2.png) [@sostock](https://discourse.julialang.org/u/sostock)\
**Post date:** [October 8, 2021, 6:57am UTC](https://discourse.julialang.org/t/mul-matrix-matrix-uppertriangular-is-really-slow/69368/5 "2021-10-08T06:57:25Z")

</div>

Issue created: [https://github.com/JuliaLang/julia/issues/42550](https://github.com/JuliaLang/julia/issues/42550)

Until this is fixed, you can use the allocating `*` instead of `mul!`, this actually uses BLAS and is indeed fast.
