Arrays 数组的快速索引
访问(或者替换)大型多维数组中的条目的最有效方法是什么?我在一个循环中使用这样的东西:Arrays 数组的快速索引,arrays,indexing,julia,Arrays,Indexing,Julia,访问(或者替换)大型多维数组中的条目的最有效方法是什么?我在一个循环中使用这样的东西: tup = (16,45,6,40,3) A[tup...] = 100 但我想知道是否有更有效的方法。特别是,是否有一种方法可以避免使用…?迭代多维数组,建议对eachindex(a)中的索引执行;见例 如果A是一个标准数组,那么这就相当于只使用单个整数进行索引,这是访问数组的最快方法(您最初的问题): 但是,如果A是一个更复杂的对象,例如子阵列,则eachindex(A)将为您提供一个不同、高效的访问
tup = (16,45,6,40,3)
A[tup...] = 100
但我想知道是否有更有效的方法。特别是,是否有一种方法可以避免使用
…
?迭代多维数组,建议对eachindex(a)中的索引执行;见例
如果A
是一个标准数组,那么这就相当于只使用单个整数进行索引,这是访问数组的最快方法(您最初的问题):
但是,如果A
是一个更复杂的对象,例如子阵列,则eachindex(A)
将为您提供一个不同、高效的访问对象:
julia> for i in eachindex(slice(A, 1:3, 2:3))
println(i)
end
给予
等等。挥霍并不一定会受到惩罚,但确定挥霍在何处有效并不总是显而易见(或容易)。您的简单示例实际上与编写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\u llvm
以获取对@jl\u pgcstack
的引用-这是垃圾收集器,任何时候都需要它。请注意,在一个更复杂的函数中,很可能还有其他东西也会导致分配,因此它的存在并不一定意味着会有一种铺天盖地的悲观情绪。但如果这是一个热循环,您希望最小化所有分配,因此这是一个很好的目标…即使您的问题不是由于浪费。您还应该使用@code\u warntype
,因为键入错误的代码肯定会使splat和许多其他操作变得悲观。如果您的元组类型不正确,将发生以下情况:
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
索引多维数组的最快方法是对其进行线性索引。
是来自Base
模块的相关函数,用于查找最有效地访问数组元素的方法
julia> a=rand(1:10...);
julia> Base.Base.linearindexing(a)
Base.LinearFast()
人们可以使用ii=sub2ind(size(A),tup…)
语法将索引的元组转换为一个线性索引,或在每个hindex(A)
中为i遍历它。对于特定条目,可以预计算ii=sub2ind(size(A),tup…
然后使用A[ii]
我相信这与A做的计算完全相同[tup…]
它正在进行相同的计算,但预计算了一次,并将计算保存在循环中(这可能是对问题的一种解释)请注意,我根本不谈论索引本身。这是因为实际的索引速度非常快。对于普通数组,全维索引(例如,5个索引)和线性索引(仅1个)之间的最大区别是五个边界检查。如果您完全确定访问是安全的,则可以使用@inbounds
避免这些边界检查。对于特殊数组类型(如子数组),避免线性索引的速度要快得多。我想我的问题是,是否有一种从元组到索引的有效方法。对于我的问题,我永远不必访问子数组
s,只需访问完整数组
的特定元素。我记得在某个地方读到过,splatting不是非常有效的(可能是Julia的早期版本)。另外,@code\u warntype
不适用于splat。@amrods-我的观点是,就您提供的示例而言,使用splat将元组解包到各个索引中绝对不涉及额外的开销。您还可以将元组包装在CartesianIndex(tup)中
,但我认为没有开销的情况将与浪费元组没有开销的情况重叠。嗯,我可能应该订阅那些公共服务公告……然而,eachindex
将为a
中的每个位置提供一个迭代器。我想我只需要一种从元组转换为索引的方法如果你也想要一个线性索引,最简单的方法就是k=0;对于eachindex(a)中的i;k+=1;诸如此类诸如此类
现在您有了线性索引和笛卡尔索引。对于许多数组类型,线性索引肯定不是索引它们的最快方法。如果您已经有了多维索引元组,使用它进行索引的最有效方法是…直接使用它进行索引!如果需要,它将是对流的但是这可能不是必需的,即使是这样,如果这不是最快的方法,那也是一个性能缺陷。
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
julia> a=rand(1:10...);
julia> Base.Base.linearindexing(a)
Base.LinearFast()