在不创建临时数组的情况下在嵌套函数中更新静态数组?<Julia>



我一直在试图在朱莉娅(Julia)使用静态阵列的墙上敲打头。

https://github.com/juliaarrays/staticarrays.jl

它们很快,但是更新它们是一种痛苦。这不足为奇,它们本来是不变的!

,但不断建议我使用静态阵列,即使我必须更新它们。就我而言,静态阵列很小,只有3个,我有一个向量,但是我一次只更新1个长度3 svector。


选项1

有一个真正整洁的软件包,称为Setfield,它允许您在朱莉娅(Julia)中进行内置更新。

https://github.com/jw3126/setfield.jl

捕获量...它更新本地副本。因此,如果您处于嵌套功能,它将更新本地副本。因此,它附带了一些书籍,因为您必须在现场更新本地副本,然后更新复制和更新实际感兴趣阵列的return。您无法传递所需的数组,并至少将其更新到位,这不是我能找到的!现在,我不介意嘘声,但是我想更新本地副本,然后返回值,更新另一个本地副本,然后返回值,最后更新实际数组,必须带有速度惩罚。我可能错了。


选项2

为了进行更新,我必须

进行更新

exampleSVector::SVector{3,Float64}&lt; - 只是为了清除其类型和大小

exampleSVector = [value1, value2, value3]

即使在函数内部,它也会更新所需的数组,这是一个不错的目标,但是如果您在功能中执行此操作,则会创建一个临时数组。这杀死了我,因为我的功能是在一个被称为4百万次的循环中,所以这会产生大量的分配并减慢一切。


如何在不创建临时数组的情况下更新选项2方案的SVector

对于选项1方案,我可以更新实际关注的数组而不是本地副本吗?

如果这需要一个简单的示例代码,请在评论中这样说,我会做一个。我的想法是,这是没有人的答案,但是如果需要的话,我会做一个。

编辑:

MCVE代码 - 选项1工作,选项2不。

using Setfield
using StaticArrays
struct Keep
    dreaming::Vector{SVector{3,Float64}}
end
function INNER!(vec::SVector{3,Float64},pre::SVector{3,Float64})
    # pretend series of calculations
    for i = 1:3 # illustrate use of Setfield (used in real code for this)
        pre = @set pre[i] = rand() * i * 1000
    end
    # more pretend calculations
    x = 25.0 # assume more calculations equals x
################## OPTION 1 ########################
    vec = @set vec = x * [ pre[1], pre[2], pre[3] ] # UNCOMMENT FOR FOR OPTION 1
    return vec                                      # UNCOMMENT FOR FOR OPTION 1
################## OPTION 2 ########################    
    #vec = x * [ pre[1], pre[2], pre[3] ]           # UNCOMMENT FOR FOR OPTION 2
    #nothing                                        # UNCOMMENT FOR FOR OPTION 2
end
function OUTER!(always::Keep)
    preAllocate = SVector{3}(0.0,0.0,0.0)
    for i=1:length(always.dreaming)
        always.dreaming[i] = INNER!(always.dreaming[i], preAllocate) # UNCOMMENT FOR FOR OPTION 1
        #INNER!(always.dreaming[i], preAllocate)                     # UNCOMMENT FOR FOR OPTION 2
    end
end
code = Keep([zero(SVector{3}) for i=1:5])
OUTER!(code)
println(code.dreaming)

我希望我能正确理解您的问题。像这样的MWE有点困难,这会做很多多余的事情,有点混乱。

这里似乎有两种替代解释:您是否确实需要更新('Mutate')SVector,但是您的MWE无法证明原因。或者,您已经说服了自己需要变异,但实际上却没有。

我决定专注于替代2:您实际上不需要"突变"。从该角度重写代码可以大大简化代码。

我找不到您在这里突变任何静态向量的任何理由,所以我只是将其删除。INNER!功能与输入的行为非常令人困惑。您提供两个输入,但不要使用其中任何一个,所以我删除了这些输入。

function inner()
    pre = @SVector [rand() * 1000i for i in 1:3]
    x = 25
    return pre .* x
end
function outer!(always::Keep)
    always.dreaming .= inner.()  # notice the dot in inner.()
end
code = Keep([zero(SVector{3}) for i in 1:5])
outer!(code)
display(code.dreaming)

这很快运行,分配为零。通常,使用静态,不要试图突变事物,只需创建新实例即可。

即使您的MWE尚不清楚,您可能想"突变" SVector的某种合理原因。在这种情况下,您可以使用setindex静态方法,您不需要setField.jl:

julia> v = rand(SVector{3})
3-element SArray{Tuple{3},Float64,1,3}:
 0.4730258499237898 
 0.23658547518737905
 0.9140206579322541 
julia> v = setindex(v, -3.1, 2)
3-element SArray{Tuple{3},Float64,1,3}:
  0.4730258499237898
 -3.1               
  0.9140206579322541

澄清:setindex(无!)不会突变其输入,而是创建一个新实例,其中一个索引值更改。

如果您确实需要"突变",也许您可以制作一个新的MWE来显示这一点。我建议您尝试简化一点,因为现在很困惑。例如,Keep类型的包含似乎完全不必要和分心。只需制作SVector S的Vector并显示您想做什么。

编辑:这是基于以下注释的尝试。据我现在,问题是要修改SVector s的向量。您无法真正突变SVector s,但是您可以使用方便的语法setindex替换它们,您可以在其中保留一些元素并更改其他元素:

oldvec = [zero(SVector{3}) for _ in 1:5]
replacevec = [rand(SVector{3}) for _ in 1:5]

现在,我们将oldvec每个元素的第二个元素替换为replacevec中的相应一个元素。首先一个单线:

oldvec .= setindex.(oldvec, getindex.(replacevec, 2), 2)

然后使用循环更快一个:

for i in eachindex(oldvec, replacevec)
    @inbounds oldvec[i] = setindex(oldvec[i], replacevec[i][2], 2)
end

有两种类型的静态阵列 - 可变(从类型名称中的M开始)和不可变的类型(以S开头) - 只需使用可变的即可!看看下面的示例:

julia> mut = MVector{3,Int64}(1:3);
julia> mut[1]=55
55
julia> mut
3-element MArray{Tuple{3},Int64,1,3}:
 55
  2
  3
julia> immut = SVector{3,Int64}(1:3);
julia> inmut[1]=55
ERROR: setindex!(::SArray{Tuple{3},Int64,1,3}, value, ::Int) is not defined.

让我们看到一些简单的基准(普通数组,与可变的静态与不可变的静态):

using BenchmarkTools
julia> ord = [1,2,3];
julia> @btime $ord.*$ord;
  39.680 ns (1 allocation: 112 bytes)
3-element Array{Int64,1}:
 1
 4
 9

julia> @btime $mut.*$mut
  8.533 ns (1 allocation: 32 bytes)
3-element MArray{Tuple{3},Int64,1,3}:
 3025
    4
    9

julia> @btime $immut.*$immut
  2.133 ns (0 allocations: 0 bytes)
3-element SArray{Tuple{3},Int64,1,3}:
 1
 4
 9