在索引处增加StaticVector

时间:2017-11-30 18:29:41

标签: arrays julia

我希望A增加位置i的静态数组x。如果它是一个可变数组,我只会做A[i] += x。但由于它是StaticArray,我需要创建一个新的。但是,如果我新增A的大小,那么我会做类似

的事情
A = A + @SVector [0,0,x]

并为每个i分配一个分支。但在这种情况下,SVector是用户输入,所以我只提前知道使用类型信息。我宁愿不让我的核心逻辑都只是一个生成函数来处理这个,所以我希望有一个简单的解决方案,或者这可能需要一个@generated辅助函数。

请注意,此问题等同于在位置@SVector创建值x的{​​{1}},否则为零。如果有一个简单的方法,那么我的问题也会得到解决。

2 个答案:

答案 0 :(得分:5)

使用array comprehensions的天真方法是

julia> k = 4 4

julia> @SVector [i == k? 1.0 : 0 for i in 1:5] 5-element SVector{10,Float64}: 0.0 0.0 0.0 1.0 0.0

在阅读自述文件的StaticArrays.jl“快速入门”部分时,这是您可以采取的良好的第一步。

但是,我们在Julia关注type stability和通用代码, 这是因为:

    函数中的
  1. 类型稳定性让编译器优化 - >速度
  2. 可以利用multiple dispatch以强大的方式重用和扩展代码。
  3. 所以更多的朱利安方式是使用

    julia> function increment_value(A::SVector{L,T},x,k) where {L,T} _A = [i == k ? x : zero(x) for i in 1:L] A+_A end

    julia> A = @SVector [0, 0, 0, 0, 10] 5-element SVector{5,Int64}: 0 0 0 0 10

    julia> increment_value(A,5,2) 5-element SVector{5,Int64}: 0 5 0 0 10

    但是,我们的最终答案应包括避免额外变量分配的方法,并利用方便的ifelse功能利用一些compiler pipelining

    `朱莉娅>使用StaticArrays,BenchmarkTools

    julia> function increment_value(A :: SVector{L,T}, x,k) where {T,L} SVector(ntuple(i->ifelse(i == k, A[i]+x, A[i]), Val{L})) end increment_value (generic function with 1 method)

    julia> a = @SVector [ 1, 2, 3, 4, 5] 5-element SVector{5,Int64}: 1 2 3 4 5

    julia> @benchmark increment_value($a,$3,$5) BenchmarkTools.Trial: memory estimate: 0 bytes allocs estimate: 0

    minimum time: 3.178 ns (0.00% GC) median time: 3.285 ns (0.00% GC) mean time: 3.293 ns (0.00% GC) maximum time: 13.620 ns (0.00% GC) samples: 10000 evals/sample: 1000

答案 1 :(得分:3)

在编译时获取此问题的所有必要值有点棘手。我现在得到的是:

'Bearer <token...>'

或仅设置坐标:

@generated updateindex(s::SVector{L,T},j::Type{Val{I}},v) where {L,T,I} = 
  Expr(:call, :(SVector{L,T}), (ifelse(i==I, :(s[$i]+v), :(s[$i])) for i=1:L)...)

这可以用作:

@generated setindex(s::SVector{L,T},j::Type{Val{I}},v) where {L,T,I} = 
  Expr(:call, :(SVector{L,T}), (ifelse(i==I, :v, :(s[$i])) for i=1:L)...)

并标记为:

julia> Z = @SVector [1,1,1,1,1];

julia> updateindex(Z,Val{3},4)
5-element SVector{5,Int64}:
 1
 1
 5
 1
 1

代码很少:

julia> using BenchmarkTools

julia> @btime updateindex($Z,Val{3},4);
  2.032 ns (0 allocations: 0 bytes)

这是否解决了这个难题?

顺便说一句,如果有办法将其重写为更易读的形式,我很乐意在评论中看到(并会相应地更新答案)。

<强>更新

Chris的评论正确地指出可以制作非值类型julia> @code_native updateindex(Z,Val{3},4) .text Filename: REPL[13] pushq %rbp movq %rsp, %rbp Source line: 1 vmovups (%rsi), %xmm0 addq 16(%rsi), %rcx movq 24(%rsi), %rax movq 32(%rsi), %rdx vmovups %xmm0, (%rdi) movq %rcx, 16(%rdi) movq %rax, 24(%rdi) movq %rdx, 32(%rdi) movq %rdi, %rax popq %rbp retq nopl (%rax) 的版本:

j

演示和低级代码(可以看出,由于不知道要更新哪个索引,必须付出一点性能):

@generated setindex(s::SVector{L,T},j,v) where {L,T} =
  Expr(:call, :(SVector{L,T}), (:(ifelse($i==j, v, s[$i])) for i=1:L)...)