【问题标题】:How much faster is Eigen for small fixed size matrices?对于固定大小的小矩阵,Eigen 的速度有多快?
【发布时间】:2016-06-03 05:42:45
【问题描述】:

我目前正在使用 Julia,但我有一个性能关键函数,它需要对固定大小的小矩阵(3 维或 4 维)进行大量重复的矩阵运算。似乎 Julia 中的所有矩阵运算都由 BLAS 和 LAPACK 后端处理。在其中一些函数中似乎还进行了大量的内存分配。

small matrices 有一个 julia 库,它拥有令人印象深刻的 3x3 矩阵加速,但它已经 3 年没有更新了。我正在考虑在Eigen 中重写我的性能关键函数

我知道 Eigen 声称对于固定大小的矩阵非常有用,但我仍在尝试判断是否应该在 Eigen 中重写此函数。性能benchmarks 适用于动态大小的矩阵。有没有人有任何数据表明一个人从固定大小的矩阵中获得了多少性能?我正在做的操作类型是矩阵x矩阵,矩阵x向量,正定线性求解。

【问题讨论】:

  • 完成一些基准测试并亲自看看。这是肯定地回答这个问题的唯一方法。确保使用优化编译 Eigen。在启用和不启用 OpenMP 的情况下尝试一下。
  • 回忆这句话的好时机:“优化的第一条规则:不要这样做!”,或者 Knuth 的明智之言:“过早的优化是万恶之源。”。积极的一面是:如果存在特定的性能瓶颈,那么在问题中分享一些代码会有所帮助。
  • 你试过Base.LinAlg.matmul3x3!这个模块的其他功能吗?它们绕过 BLAS 并允许最少的分配计算。
  • 这个很有意思,没试过,我看看。之后还有其他专门针对 3x3 矩阵的函数吗?
  • 我想你的答案就在这里:github.com/SimonDanisch/FixedSizeArrays.jl

标签: julia eigen blas eigen3


【解决方案1】:

如果您想对小矩阵进行快速运算,我强烈推荐StaticArrays。例如(注意:这最初是在 BenchmarkTools 包之前编写的,现在推荐使用):

using StaticArrays
using LinearAlgebra

function foo(A, b, n)
    s = 0.0
    for i = 1:n
        s += sum(A*b)
    end
    s
end

function foo2(A, b, n)
    c = A*b
    s = 0.0
    for i = 1:n
        mul!(c, A, b)
        s += sum(c)
    end
    s
end

A = rand(3,3)
b = rand(3)
Af = SMatrix{3,3}(A)
bf = SVector{3}(b)

foo(A, b, 1)
foo2(A, b, 1)
foo(Af, bf, 1)

@time foo(A, b, 10^6)
@time foo2(A, b, 10^6)
@time foo(Af, bf, 10^6)

结果:

julia> include("/tmp/foo.jl")
  0.080535 seconds (1.00 M allocations: 106.812 MiB, 14.86% gc time)
  0.064963 seconds (3 allocations: 144 bytes)
  0.001719 seconds (2 allocations: 32 bytes)

foo2 试图聪明并避免内存分配,但在使用 StaticArrays 时,它被天真的实现所震撼。

【讨论】:

  • 感谢您的回答蒂姆,到目前为止一切顺利,现在只有一件事阻碍了我。我有数以万计的 3x3 和 3x1,但它们在我的算法的每次迭代中都在变化。理想情况下,一旦它们被创建,我想通过使用 setindex 来更改它们!即 bf[1]=44,但该软件包尚不支持此功能。我该怎么办?
  • 首先将它们创建为Fixed。例如,不是从函数返回[a,b,c],而是返回Vec((a,b,c))。那会快得多。通常你不需要setindex!,只需替换整个矩阵。 (有时 LLVM 会将其实现为引擎盖下的替代品,但不看你就不会知道。)
  • 我的算法中有大量固定大小的矩阵和向量,它们在每次迭代时都会发生变化。目前我有一个像 x=Array{FixedSizeArrays.Vec{2,Float64},1}() 这样的数组。如果我调整它的大小,那么这些人会被初始化,并且赋值 x[1]=Mat(..) 将不再起作用。同样,我需要在我的 for 循环的每次迭代中创建这个数组 x,因为它是一个大数组,这将是一个巨大的内存分配负担。我想在跳转到我的 for 循环之前分配这个数组 x,然后能够修改 x[i]。
  • 我还按照您的建议重写了我的代码,方法是执行 sizehint!(x,n) 然后 push!(x,Mat(...)) 因为我无法修改已初始化的 Mats。使用 FixedSizeArrays,我的代码运行时间要长 7 倍。在分析过程中,大部分时间都花在了 constructor.jl 中。看来 Vec((a,b,c)) 正在杀死它。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-02-13
  • 2021-12-23
  • 2014-11-28
  • 1970-01-01
  • 1970-01-01
  • 2016-08-12
  • 2023-03-20
相关资源
最近更新 更多