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 分配了一点点。但是你做基准测试的方式会给你非常错误的答案,特别是当你在全球范围内进行基准测试时,内存估计会偏离。一些提示:
- 使用 BenchmarkTools.jl 进行基准测试。
- 您不需要可变结构。您可以在不可变结构中改变数组。
- 狡辩,但使用
x::Array{Int, 1}
或 x::Vector{Int}
而不是 Array{Int, 2}
。
- 除非你真的需要,否则不要在函数中放置
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)
考虑以下示例:
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 分配了一点点。但是你做基准测试的方式会给你非常错误的答案,特别是当你在全球范围内进行基准测试时,内存估计会偏离。一些提示:
- 使用 BenchmarkTools.jl 进行基准测试。
- 您不需要可变结构。您可以在不可变结构中改变数组。
- 狡辩,但使用
x::Array{Int, 1}
或x::Vector{Int}
而不是Array{Int, 2}
。 - 除非你真的需要,否则不要在函数中放置
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)