访问(也许替换)大型多维数组中的条目的最有效方法是什么?我在循环中使用这样的东西:
tup = (16,45,6,40,3) A[tup...] = 100
但我想知道是否有更有效的方法.特别是,有什么方法可以避免使用...
?
要迭代多维数组,建议这样做for index in eachindex(A)
; 见例如
https://groups.google.com/forum/#!msg/julia-users/CF_Iphgt2Wo/V-b31-6oxSkJ
如果A
是标准数组,那么这对应于使用单个整数进行索引,这是访问数组的最快方式(原始问题):
A = rand(3, 3) for i in eachindex(A) println(i) end
但是,如果A
是一个更复杂的对象,例如子阵列,那么eachindex(A)
将为您提供一个不同的,高效的访问对象:
julia> for i in eachindex(slice(A, 1:3, 2:3)) println(i) end
给
CartesianIndex{2}((1,1)) CartesianIndex{2}((2,1))
等等
splatting并不总是会受到惩罚,但确定它的效率并不总是显而易见(或容易).你的琐碎例子实际上和写作一样有效A[16,45,6,40,3] = 100
.你可以通过比较看到这一点
function f(A) tup = (16,45,6,40,3) A[tup...] = 100 A end function g(A) A[16,45,6,40,3] = 100 A end julia> code_llvm(f, Tuple{Array{Int, 5}}) # Lots of output (bounds checks). julia> code_llvm(g, Tuple{Array{Int, 5}}) # Identical to above
如果有一个泼溅惩罚,你会以分配的形式看到它.您可以使用@allocated
宏来测试这个,或者只是检查code_llvm
一个引用@jl_pgcstack
- 这就是垃圾收集器,这在任何时候都需要分配.请注意,在更复杂的函数中很可能还会导致分配,因此它的存在并不一定意味着存在splatting悲观化.但如果这是一个热循环,你想最小化所有分配,所以它是一个伟大的目标......即使你的问题不是由于splatting.您也应该使用@code_warntype
,因为类型不好的代码肯定会使splats和许多其他操作失望.如果您的元组输入不好,将会发生以下情况:
function h(A) tup = ntuple(x->x+1, 5) # type inference doesn't know the type or size of this tuple A[tup...] = 100 A end julia> code_warntype(h, Tuple{Array{Int,5}}) # Lots of red flags
所以优化这个splat将高度依赖于你如何构建或获得tup
.