Julia - 有没有办法避免在不使用太多内存的情况下使用循环

时间:2017-09-24 17:42:51

标签: performance julia allocation

考虑以下示例:

module structs
mutable struct testStruct
    x::Array{Int64,2}
end
end

function innerFunc(s::structs.testStruct)
    s.x[1:2] .= s.x[3:4]
end

function structTest!(s::structs.testStruct, n)
    for i = 1:n
        innerFunc(s)
    end
    println(s.x)
end

随着我增加n,内存分配也会增加。我认为这是因为在每次迭代中,我都在为s.x[3:4]创建分配。我可以通过使用循环来避免这种情况:

function innerFunc(s::structs.testStruct)
    for i = 1:2
        s.x[i] .= s.x[i+2]
    end
end

function structTest!(s::structs.testStruct, n)
    for i = 1:n
        innerFunc(s)
    end
    println(s.x)
end

但是,我不喜欢循环,因为语法很麻烦。有没有办法避免它?在每次迭代中,我想修改s.x的第一个和第二个元素而不增加内存分配,因为我增加了n,因为我没有创建任何新内容。

更新:为了回应DNF,我尝试使用@view

module structs
mutable struct testStruct
    x::Array{Int64,2}
end
end

function innerfunc!(s::structs.testStruct)
    s.x[1:2] .= view(s.x, 3:4)
end

function structTest!(s::structs.testStruct, n)
    for i = 1:n
        innerfunc!(s)
    end
    println(s.x)
end

这就是我得到的:

@time structTest!(structs.testStruct([1 2 3 4]),33)
0.000112 seconds (202 allocations: 7.938 KiB)

@time structTest!(structs.testStruct([1 2 3 4]),330)
0.000126 seconds (1.69 k allocations: 68.266 KiB)

我希望分配对n保持不变。

2 个答案:

答案 0 :(得分:2)

使用view

function innerfunc!(s::structs.testStruct)
    s.x[1:2] .= view(s.x, 3:4)
end

function innerfunc!(s::structs.testStruct)
    @views s.x[1:2] .= s.x[3:4]
end

哦,并在函数名中添加!,因为它会改变输入。

编辑:显然,我错了。视图分配一点点。但是你做基准测试的方式会给你非常错误的答案,特别是当你在全球范围内进行基准测试时,内存估计是偏离的。一些提示:

  1. 使用BenchmarkTools.jl进行基准测试。
  2. 您不需要可变结构。你可以改变一个不可变结构中的数组。
  3. Quibbling,但请使用x::Array{Int, 1}x::Vector{Int}代替Array{Int, 2}
  4. 除非你真的需要,否则不要在函数中放置print语句!
  5. struct TestStruct
        x::Vector{Int64}
    end
    
    function innerfunc!(s::TestStruct)
        s.x[1:2] .= view(s.x, 3:4)
    end
    
    function structtest!(s::TestStruct, n)
        for i = 1:n
            innerfunc!(s)
        end
        return s
    end
    
    julia> s = TestStruct([1, 2, 3, 4])
    TestStruct([1, 2, 3, 4])
    
    julia> @btime structtest!($s, 33)
      575.108 ns (33 allocations: 1.55 KiB)
    TestStruct([3, 4, 3, 4])
    

答案 1 :(得分:2)

正如@DNF所指出的,使用视图应该已经解决了问题,但由于视图不是不可变的,因此存储器成本很低,这在通常的应用程序中并不重要。

基本上,你必须使用for循环。为了让它看起来有点矢量化,Dahua Lin的Devectorized.jl软件包得以拯救:

# Pkg.add("Devectorized.jl")
using Devectorized

function innerfunc!(s::structs.testStruct)
    @devec s.x[1:2] = s.x[3:4]
end

你很高兴:

julia> @time structTest!(structs.testStruct([1 2 3 4]),3300)
  0.000299 seconds (40 allocations: 1.641 KiB)

julia> @time structTest!(structs.testStruct([1 2 3 4]),330000)
  0.001209 seconds (40 allocations: 1.641 KiB)