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

Julia - Is there a way to avoid using a loop without using too much memory

考虑以下示例:

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

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

更新:为了响应 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

使用 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

哦,在函数名中放一个 !,因为它会改变输入。

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

  1. 使用 BenchmarkTools.jl 进行基准测试。
  2. 您不需要可变结构。您可以在不可变结构中改变数组。
  3. 狡辩,但使用 x::Array{Int, 1}x::Vector{Int} 而不是 Array{Int, 2}
  4. 除非你真的需要,否则不要在函数中放置 print 语句!

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])

正如@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)