Memory Julia嵌套循环占用了大量内存

Memory Julia嵌套循环占用了大量内存,memory,memory-management,julia,Memory,Memory Management,Julia,我有一个嵌套循环迭代方案,它占用了大量内存。我知道我不应该有全局变量,但即使在我将所有内容都包含在一个函数中,记忆情况也没有改善。它只是在每次迭代后累积,就好像没有垃圾回收一样 下面是一个与我的代码类似的可行示例 我有两个文件。首先,函数.jl: ##functions.jl module functions function getMatrix(A) L = rand(A,A); return L; end function loopOne(A, B) res =

我有一个嵌套循环迭代方案,它占用了大量内存。我知道我不应该有全局变量,但即使在我将所有内容都包含在一个函数中,记忆情况也没有改善。它只是在每次迭代后累积,就好像没有垃圾回收一样

下面是一个与我的代码类似的可行示例

我有两个文件。首先,
函数.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
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)
第二个文件
main.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
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
时,内存会随着我在
main(C)
中扩展
C
而增加(当我将
C
增加到1000000时,有时会增加到数百万个分配和10GB以上)

我知道这个例子看起来毫无用处,但它与我的结构相似。有人能帮忙吗?多谢各位

更新:

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
后是否有办法“清除”内存?由于我没有创建任何新内容或存储任何新内容,因此内存使用量应该保持不变。

至少有几个指针:

  • getMatrix函数每次分配一个新的AxA矩阵。这肯定会消耗内存。如果可以,最好避免分配,例如使用
    rand
    使用随机值填充现有数组

  • res=0
    行将
    res
    定义为
    Int
    ,但随后将
    矩阵{Float}
    分配给它(结果是
    inv(getMatrix)
    )。更改代码中变量的类型会使编译器很难确定变量的类型,这会导致代码速度变慢

  • 似乎您有一个名为
    函数
    的模块,但您没有编写它

  • 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++中为常量)

  • 使用
    =
    操作符指示您不是在创建新容器,而是在覆盖现有容器的元素。
    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中分配空间是计算中最昂贵的操作之一,因此减少分配是加速代码的一个好方法。但你无法避免


    因此,您的代码(采用新格式)没有任何问题—您看到的内存分配和占用的时间只是执行大型(昂贵)操作的结果

    您的代码在我的julia笔记本435ms和252MiB(julia 0.6)中稍作修改(对外部文件的引用)后就完成了,您的安装还存在其他问题顺便说一句,您不需要在中使用分号julia@stefanbachert当增加循环数时,它是否会改变?另外,当我尝试在不执行inv的情况下使用相同的函数时,我的代码要快得多。inv有什么特别不对劲的地方吗?有趣的是,看到Julia像C++一样编写,这也是交叉发布到的;这里也有一些讨论。即使我将
    rand(A,A)
    更改为
    [1 2;3 4]
    ,我仍然看到同样的情况,即即使不使用
    rand(A,A)
    ,内存消耗也会增加。4.我知道这没什么用。这只是一个可行的例子。既然我没有创建任何新的东西,内存消耗难道不应该保持不变吗?所以我应该在循环外创建一个虚拟变量,并允许循环内的操作“填充”虚拟变量?对不起!在我读到你的评论之前,我发布了这篇文章。当我完成后,我会给你一个投票。在我看来,这是一个非常清楚的答案,即使没有评论。你这么认为?那我就改变那一点建议。