访问(并且可能替换)大型多维数组中的条目的最有效方法是什么?我在循环中使用这样的东西:
tup = (16,45,6,40,3)
A[tup...] = 100
但我想知道是否有更有效的方法。特别是,有没有办法可以避免使用...
?
答案 0 :(得分:5)
要迭代多维数组,建议执行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))
等
答案 1 :(得分:5)
并不总是涉及泼溅的惩罚,但确定它的效率并不总是显而易见(或容易)。您的简单示例实际上与编写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
的方式。
答案 2 :(得分:-1)
索引多维数组的最快方法是线性索引
Base.Base.linearindexing是Base
模块中的一个相关函数,用于查找最有效的访问方式到数组元素。
julia> a=rand(1:10...);
julia> Base.Base.linearindexing(a)
Base.LinearFast()
可以使用ii=sub2ind(size(A),tup...)
语法将索引元组转换为一个线性索引或for i in eachindex(A)
来遍历它。