我有一个嵌套循环迭代方案,它占用了大量内存。我确实理解我不应该使用全局变量,但即使我将所有内容都包含在函数中,内存情况也没有改善。它只是在每次迭代后累积,就好像没有垃圾收集一样。
这是一个可行的例子,与我的代码相似。
我有两个文件。首先,functions.jl
:
##functions.jl
module functions
function getMatrix(A)
L = rand(A,A);
return L;
end
function loopOne(A, B)
res = 0;
for i = 1:B
res = inv(getMatrix(A));
end
return(res);
end
end
第二个文件main.jl
:
##main.jl
include("functions.jl")
function main(C)
res = 0;
A = 50;
B = 30;
for i =1:C
mat = functions.loopOne(A,B);
res = mat .+ 1;
end
return res;
end
main(100)
当我执行julia main.jl
时,随着C
中main(C)
的扩展,内存会增加(当我将C
增加到1000000时,有时会超过数百万次分配和10GiB)
我知道这个例子看起来没用,但它类似于我的结构。有人可以帮忙吗?谢谢。
更新:
Michael K. Borregaard给出了一个非常有用的答案:
module Functions #1
function loopOne!(res, mymatrix, B) #2
for i = 1:B
res .= inv(rand!(mymatrix)) #3
end
return res #4
end
end
function some_descriptive_name(C) #5
A, B = 50, 30 #6
res, mymat = zeros(A,A), zeros(A,A)
for i =1:C
res .= Functions.loopOne!(res, mymat, B) .+ 1
end
return res
然而,当我计时时,拨号C
时,分配和内存仍会增加。
@time some_descriptive_name(30)
0.057177 seconds (11.77 k allocations: 58.278 MiB, 9.58% gc time)
@time some_descriptive_name(60)
0.113808 seconds (23.53 k allocations: 116.518 MiB, 9.63% gc time)
我认为问题来自inv
函数。如果我将代码更改为:
function some_descriptive_name(C) #5
A, B = 50, 30 #6
res, mymat = zeros(A,A), zeros(A,A)
for i =1:C
res .= res .+ 1
end
return res
end
然后内存和分配将保持不变:
@time some_descriptive_name(3)
0.000007 seconds (8 allocations: 39.438 KiB)
@time some_descriptive_name(60)
0.000037 seconds (8 allocations: 39.438 KiB)
有没有办法清除"清除"使用inv
后的内存?由于我没有创建任何新内容或存储任何新内容,因此内存使用率应保持不变。
答案 0 :(得分:11)
至少有几点指示:
getMatrix函数每次都会分配一个新的AxA矩阵。这肯定会消耗内存。如果可以的话,最好避免分配,例如使用rand!
用随机值填充现有数组。
res = 0
行将res
定义为Int
,但您随后为其分配Matrix{Float}
(inv(getMatrix)
的结果)。更改代码中变量的类型使编译器很难弄清楚类型是什么,这会导致代码变慢。
您似乎有一个名为functions
的模块,但您不会写它。
res = inv
代码行不断覆盖该值,因此循环不执行任何操作!
结构和代码看起来像C ++。试着看风格指南。
以下是代码以更加理想化的方式看起来如何避免分配:
module Functions #1
function loopOne!(res, mymatrix, B) #2
for i = 1:B
res .= inv(rand!(mymatrix)) #3
end
return res #4
end
end
function some_descriptive_name(C) #5
A, B = 50, 30 #6
res, mymat = zeros(A,A), zeros(A,A)
for i =1:C
res .= Functions.loopOne!(res, mymat, B) .+ 1
end
return res
end
评论:
如果您愿意,可以使用模块 - 您是否将文件放在不同的文件中取决于您自己。模块名称大写。
如果可以,使用覆盖现有容器值的函数是一个优势。这些函数以!
结束,表示它们将修改参数(比如通过引用可变地传递,而不是在C ++中使用const)。
使用.=
运算符表示您没有创建新容器,而是覆盖现有容器的元素。 rand!
函数会覆盖mymatrix
。
不严格需要return关键字,但DNF建议在评论中使用更好的样式
在Julia中没有使用main
约定,因为大多数代码都是由用户调用的,而不是通过执行程序调用的。
多个变量的紧凑分配格式。
请注意,在这种情况下,这些优化都不重要,因为99%的计算时间花费在昂贵的inv
函数上。
对更新的回应:
inv函数没有任何问题,这只是一项代价高昂的操作。但我想你可能会误解记忆计数的作用。并不是说内存使用量正在增加,就像你在C ++中寻找的那样,如果你有一个指向永不释放的对象的指针(内存泄漏)。内存使用是不变的,但分配的总和会增加,因为inv
函数必须进行一些内部分配。
考虑这个例子
for i in 1:n
b = [1, 2, 3, 4] # Here a length-4 Array{Int64} is initialized in memory, cost is 32 bytes
end # Here, that memory is released.
对于每次运行for循环,分配32个字节,释放32个字节。当循环结束时,无论n如何,都将从该操作分配0个字节。但是Julia的内存跟踪只会增加分配 - 所以在运行代码后你会看到32 * n字节的分配。 julia这样做的原因是在RAM中分配空间是计算中最昂贵的操作之一 - 因此减少分配是加速代码的好方法。但你无法避免它。
因此您的代码没有任何问题(以新格式) - 您看到的内存分配和时间只是执行大型(昂贵)操作的结果。