因此,我正在编写一个程序,该程序需要我分配和求和一个大矩阵 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()中进行额外的内存分配。它还可以加强该功能。
答案 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尝试针对特定用例高度优化代码,因此,为了为您的用例获得确定的答案,问题的详细说明至关重要。如果您愿意分享,可以给出更具体的答案。