我试图通过预先分配数组来最小化 Julia 中的内存分配,如图所示 in the documentation .我的示例代码如下所示:
using BenchmarkTools
dim1 = 100
dim2 = 1000
A = rand(dim1,dim2)
B = rand(dim1,dim2)
C = rand(dim1,dim2)
D = rand(dim1,dim2)
M = Array{Float64}(undef,dim1,dim2)
function calc!(a, b, c, d, E)
@. E = a * b * ((d-c)/d)
nothing
end
function run_calc(A,B,C,D,M)
for i in 1:dim2
@views calc!(A[:,i], B[:,i], C[:,i], D[:,i], M[:,i])
end
end
我的理解是,这基本上不应该分配,因为 M
在两个函数中的任何一个之外预先分配。但是,当我对此进行基准测试时,我仍然看到很多分配:@btime run_calc(A,B,C,D,M)
1.209 ms (14424 allocations: 397.27 KiB)
在这种情况下,我当然可以运行更简洁的
@btime @. M = A * B * ((D-C)/D)
它按预期执行很少的分配:122.599 μs (6 allocations: 144 bytes)
但是我的实际代码更复杂,不能像这样简化,因此我想知道第一个版本哪里出了问题。
最佳答案
你没有做错任何事。目前在 Julia 中创建 View 正在分配(正如 Stefan 指出的,它比过去好得多,但在这种情况下似乎仍然发生了一些分配)。您看到的分配是由此产生的结果。
看:
julia> @allocated view(M, 1:10, 1:10)
64
您的情况是编写适当循环最简单的情况之一(我假设在您的代码中循环会更复杂,但我希望意图很明确),例如:julia> function run_calc2(A,B,C,D,M)
@inbounds for i in eachindex(A,B,C,D,M)
M[i] = A[i] * B[i] * ((D[i] - C[i])/D[i])
end
end
run_calc2 (generic function with 1 method)
julia> @btime run_calc2($A,$B,$C,$D,$M)
56.441 μs (0 allocations: 0 bytes)
julia> @btime run_calc($A,$B,$C,$D,$M)
893.789 μs (14424 allocations: 397.27 KiB)
julia> @btime @. $M = $A * $B * (($D-$C)/$D);
381.745 μs (0 allocations: 0 bytes)
编辑:Julia 版本 1.6.0-DEV.1580 上的所有时间EDIT2:为了完整性,代码通过
@views
深入到内部功能。它仍然分配(但更好)并且仍然比仅使用循环慢:julia> function calc2!(a, b, c, d, E, i)
@inbounds @. @views E[:,i] = a[:,i] * b[:,i] * ((d[:,i]-c[:,i])/d[:,i])
nothing
end
calc2! (generic function with 1 method)
julia> function run_calc3(A,B,C,D,M)
for i in 1:dim2
calc2!(A,B,C,D,M,i)
end
end
run_calc3 (generic function with 1 method)
julia> @btime run_calc3($A,$B,$C,$D,$M);
305.709 μs (1979 allocations: 46.56 KiB)
关于memory-management - Julia 中的预分配,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65131778/