JULIA上的Inv()与'\'

时间:2019-02-26 16:50:25

标签: matrix julia

因此,我正在编写一个程序,该程序需要我分配和求和一个大矩阵 M ,并将其每次转换为100 x 100,并重复大约1000次。

我最初使用inv()函数,但是由于要花费大量时间,因此我想优化程序以使其运行更快。因此,我编写了一些虚拟代码来测试可能会降低速度的东西:

function test1()
  for i in (1:100)
    =Diagonal(rand(100))  
    inverse_=inv(B)
 end
end

using BenchmarkTools
@benchmark test1()
---------------------------------
BenchmarkTools.Trial: 
memory estimate:  178.13 KiB
allocs estimate:  400
--------------
minimum time:     67.991 μs (0.00% GC)
median time:      71.032 μs (0.00% GC)
mean time:        89.125 μs (19.43% GC)
maximum time:     2.490 ms (96.64% GC)
--------------
samples:          10000
evals/sample:     1

当我使用'\'运算符求逆时:

function test2()
for i in (1:100)
    =Diagonal(rand(100))  
   inverse_=\Diagonal(ones(100))
 end
end
using BenchmarkTools
@benchmark test2()
-----------------------------------
BenchmarkTools.Trial: 
memory estimate:  267.19 KiB
allocs estimate:  600
--------------
minimum time:     53.728 μs (0.00% GC)
median time:      56.955 μs (0.00% GC)
mean time:        84.430 μs (30.96% GC)
maximum time:     2.474 ms (96.95% GC)
--------------
samples:          10000
evals/sample:     1

我可以看到inv()占用的内存少于'\'运算符,尽管最后'\'运算符更快。

这是因为我正在使用额外的恒等矩阵-> test2()中的Diagonal(ones(100))吗?这是否意味着每次运行循环时,都会分配新的内存部分来存储Identity矩阵?

我的原始矩阵是三角矩阵。反转具有如此大量零的矩阵是否会花费更多的内存分配?对于这样的矩阵,最好使用什么:inv()或'\'函数,或者还有其他更好的策略吗?

P.S:julia中的求逆矩阵与C和Python等其他语言相比如何?当我在用C编写的旧程序中运行相同的算法时,所花费的时间要少得多……所以我想知道inv()函数是否是罪魁祸首。


编辑:

因此,正如我指出的那样,我在键入test1()函数时输入了错误。其实是

function test1()
for i in (1:100)
   =Diagonal(rand(100))  
   inverse_=inv()
  end
end

但是我的问题仍然存在,test1()函数分配的内存更少,但需要更多的时间:

using BenchmarkTools
@benchmark test1()

>BenchmarkTools.Trial: 
memory estimate:  178.13 KiB
allocs estimate:  400
--------------
minimum time:     68.640 μs (0.00% GC)
median time:      71.240 μs (0.00% GC)
mean time:        90.468 μs (20.23% GC)
maximum time:     3.455 ms (97.41% GC)

samples:          10000
evals/sample:     1

using BenchmarkTools

@benchmark test2()

BenchmarkTools.Trial: 
memory estimate:  267.19 KiB
allocs estimate:  600
--------------
minimum time:     54.368 μs (0.00% GC)
median time:      57.162 μs (0.00% GC)
mean time:        86.380 μs (31.68% GC)
maximum time:     3.021 ms (97.52% GC)
--------------
samples:          10000
evals/sample:     1

我还测试了test2()函数的其他一些变体:

function test3()
for i in (1:100)
   =Diagonal(rand(100)) 
    =Diagonal(ones(100))
   inverse_=\
 end
end


function test4()
for i in (1:100)
     =Diagonal(rand(100))  
   inverse_=\
end
end

using BenchmarkTools

@benchmark test3()

>BenchmarkTools.Trial: 
 memory estimate:  267.19 KiB
allocs estimate:  600
 --------------
minimum time:     54.248 μs (0.00% GC)
median time:      57.120 μs (0.00% GC)
mean time:        86.628 μs (32.01% GC)
maximum time:     3.151 ms (97.23% GC)
 --------------
samples:          10000
evals/sample:     1

using BenchmarkTools

@benchmark test4(Diagonal(ones(100)))

>BenchmarkTools.Trial: 
 memory estimate:  179.02 KiB
 allocs estimate:  402
 --------------
 minimum time:     48.556 μs (0.00% GC)
 median time:      52.731 μs (0.00% GC)
 mean time:        72.193 μs (25.48% GC)
 maximum time:     3.015 ms (97.32% GC)
 --------------
 samples:          10000
 evals/sample:     1

test2()和test3()是等效的。我意识到我可以像在test4()中那样,通过将Identity矩阵作为变量传递,从而在test2()中进行额外的内存分配。它还可以加强该功能。

1 个答案:

答案 0 :(得分:3)

您提出的问题很棘手,取决于具体情况。我可以根据您的情况回答您,但是如果您发布真正的问题,答案可能会更改。

因此,对于您的问题,代码不是等效的,因为首先在B中使用一些矩阵inv(B),该矩阵是未定义的(可能是全局的,类型不稳定,变量),如果将B更改为,则实际上第一个代码要快一些:

julia> function test1()
         for i in (1:100)
           =Diagonal(rand(100))
           inverse_=inv()
        end
       end
test1 (generic function with 1 method)

julia> function test2()
       for i in (1:100)
           =Diagonal(rand(100))
          inverse_=\Diagonal(ones(100))
        end
       end
test2 (generic function with 1 method)

julia> using BenchmarkTools

julia> @benchmark test1()
BenchmarkTools.Trial:
  memory estimate:  178.13 KiB
  allocs estimate:  400
  --------------
  minimum time:     28.273 μs (0.00% GC)
  median time:      32.900 μs (0.00% GC)
  mean time:        43.447 μs (14.28% GC)
  maximum time:     34.779 ms (99.70% GC)
  --------------
  samples:          10000
  evals/sample:     1

julia> @benchmark test2()
BenchmarkTools.Trial:
  memory estimate:  267.19 KiB
  allocs estimate:  600
  --------------
  minimum time:     28.273 μs (0.00% GC)
  median time:      33.928 μs (0.00% GC)
  mean time:        45.907 μs (15.25% GC)
  maximum time:     34.718 ms (99.74% GC)
  --------------
  samples:          10000
  evals/sample:     1

现在,第二件事是您的代码使用对角矩阵,而Julia足够聪明,可以为inv\使用专门的方法来处理这种矩阵。它们的定义如下:

(\)(Da::Diagonal, Db::Diagonal) = Diagonal(Da.diag .\ Db.diag)

function inv(D::Diagonal{T}) where T
    Di = similar(D.diag, typeof(inv(zero(T))))
    for i = 1:length(D.diag)
        if D.diag[i] == zero(T)
            throw(SingularException(i))
        end
        Di[i] = inv(D.diag[i])
    end
    Diagonal(Di)
end

您会看到这样的示例不能完全代表一般情况(如果矩阵不是对角线,则可以使用其他方法)。您可以检查使用哪种方法,如下所示:

julia> @which �\Diagonal(ones(100))
\(Da::Diagonal, Db::Diagonal) in LinearAlgebra at C:\cygwin\home\Administrator\buildbot\worker\package_win64\build\usr\share\julia\stdlib\v1.1\LinearAlgebra\src\diagonal.jl:493

julia> @which inv(�)
inv(D::Diagonal{T,V} where V<:AbstractArray{T,1}) where T in LinearAlgebra at C:\cygwin\home\Administrator\buildbot\worker\package_win64\build\usr\share\julia\stdlib\v1.1\LinearAlgebra\src\diagonal.jl:496

然后自己查找代码。

我假设您在实际运动中没有对角矩阵。特别是如果您有块矩阵,则可以看看https://github.com/JuliaArrays/BlockArrays.jl软件包,因为它可能具有针对您的用例的优化方法。您也可以看看https://github.com/JuliaMatrices/BandedMatrices.jl

总而言之-您可以期望Julia尝试针对特定用例高度优化代码,因此,为了为您的用例获得确定的答案,问题的详细说明至关重要。如果您愿意分享,可以给出更具体的答案。