Skip to content

Commit 7f2d845

Browse files
committed
Refactor SubArray indexing to use Varargs
1 parent 51b4bc1 commit 7f2d845

File tree

4 files changed

+75
-149
lines changed

4 files changed

+75
-149
lines changed

base/multidimensional.jl

Lines changed: 0 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -458,95 +458,6 @@ for (f, fmod, op) = ((:cummin, :_cummin!, :min), (:cummax, :_cummax!, :max))
458458
@eval ($f)(A::AbstractArray) = ($f)(A, 1)
459459
end
460460

461-
## SubArray index merging
462-
# A view created like V = A[2:3:8, 5:2:17] can later be indexed as V[2:7],
463-
# creating a new 1d view.
464-
# In such cases we have to collapse the 2d space spanned by the ranges.
465-
#
466-
# API:
467-
# merge_indexes(V, indexes::NTuple, index)
468-
# indexes encodes the view's trailing indexes into the parent array,
469-
# and index encodes the subset of these elements that we'll select.
470-
#
471-
# It returns a CartesianIndex or array of CartesianIndexes.
472-
473-
# Checking 'in' a range is fast -- so check all possibilities and keep the good ones
474-
@generated function merge_indexes{N}(V, indexes::NTuple{N}, index::Union{Colon, Range})
475-
# There may be a vector of cartesian indices in the passed indexes... which
476-
# makes the number of indices more than N. Since we pre-allocate the array
477-
# of CartesianIndexes, we need to figure out how big to make it
478-
M = 0
479-
for T in indexes.parameters
480-
T <: CartesianIndex ? (M += length(T)) : (M += 1)
481-
end
482-
index_length_expr = index <: Colon ? Symbol("Istride_", N+1) : :(length(index))
483-
quote
484-
Cartesian.@nexprs $N d->(I_d = indexes[d])
485-
dimlengths = Cartesian.@ncall $N index_lengths_dim V.parent length(V.indexes)-N+1 I
486-
Istride_1 = 1 # strides of the indexes to merge
487-
Cartesian.@nexprs $N d->(Istride_{d+1} = Istride_d*dimlengths[d])
488-
idx_len = $(index_length_expr)
489-
if idx_len < 0.1*$(Symbol("Istride_", N+1)) # this has not been carefully tuned
490-
return merge_indexes_div(V, indexes, index, dimlengths)
491-
end
492-
Cartesian.@nexprs $N d->(counter_d = 1) # counter_0 is the linear index
493-
k = 0
494-
merged = Array(CartesianIndex{$M}, idx_len)
495-
Cartesian.@nloops $N i d->(1:dimlengths[d]) d->(counter_{d-1} = counter_d + (i_d-1)*Istride_d; @inbounds idx_d = I_d[i_d]) begin
496-
if counter_0 in index # this branch is elided for ::Colon
497-
@inbounds merged[k+=1] = Cartesian.@ncall $N CartesianIndex{$M} idx
498-
end
499-
end
500-
merged
501-
end
502-
end
503-
504-
# mapping getindex across the parent and subindices rapidly gets too big to
505-
# automatically inline, but it is crucial that it does so to avoid allocations
506-
# Unlike SubArray's reindex, merge_indexes doesn't drop any indices.
507-
@inline inlinemap(f, t::Tuple, s::Tuple) = (f(t[1], s[1]), inlinemap(f, tail(t), tail(s))...)
508-
inlinemap(f, t::Tuple{}, s::Tuple{}) = ()
509-
inlinemap(f, t::Tuple{}, s::Tuple) = ()
510-
inlinemap(f, t::Tuple, s::Tuple{}) = ()
511-
512-
# Otherwise, we fall back to the slow div/rem method, using ind2sub.
513-
@inline merge_indexes{N}(V, indexes::NTuple{N}, index) =
514-
merge_indexes_div(V, indexes, index, index_lengths_dim(V.parent, length(V.indexes)-N+1, indexes...))
515-
516-
@inline merge_indexes_div{N}(V, indexes::NTuple{N}, index::Real, dimlengths) =
517-
CartesianIndex(inlinemap(getindex, indexes, ind2sub(dimlengths, index)))
518-
merge_indexes_div{N}(V, indexes::NTuple{N}, index::AbstractArray, dimlengths) =
519-
reshape([CartesianIndex(inlinemap(getindex, indexes, ind2sub(dimlengths, i))) for i in index], size(index))
520-
merge_indexes_div{N}(V, indexes::NTuple{N}, index::Colon, dimlengths) =
521-
[CartesianIndex(inlinemap(getindex, indexes, ind2sub(dimlengths, i))) for i in 1:prod(dimlengths)]
522-
523-
# Merging indices is particularly difficult in the case where we partially linearly
524-
# index through a multidimensional array. It's easiest if we can simply reduce the
525-
# partial indices to a single linear index into the parent index array.
526-
function merge_indexes{N}(V, indexes::NTuple{N}, index::Tuple{Colon, Vararg{Colon}})
527-
shape = index_shape(indexes[1], index...)
528-
reshape(merge_indexes(V, indexes, :), (shape[1:end-1]..., shape[end]*prod(index_lengths_dim(V.parent, length(V.indexes)-length(indexes)+2, tail(indexes)...))))
529-
end
530-
@inline merge_indexes{N}(V, indexes::NTuple{N}, index::Tuple{Real, Vararg{Real}}) = merge_indexes(V, indexes, sub2ind(size(indexes[1]), index...))
531-
# In general, it's a little trickier, but we can use the product iterator
532-
# if we replace colons with ranges. This can be optimized further.
533-
function merge_indexes{N}(V, indexes::NTuple{N}, index::Tuple)
534-
I = replace_colons(V, indexes, index)
535-
shp = index_shape(indexes[1], I...) # index_shape does no bounds checking
536-
dimlengths = index_lengths_dim(V.parent, length(V.indexes)-N+1, indexes...)
537-
sz = size(indexes[1])
538-
reshape([CartesianIndex(inlinemap(getindex, indexes, ind2sub(dimlengths, sub2ind(sz, i...)))) for i in product(I...)], shp)
539-
end
540-
@inline replace_colons(V, indexes, I) = replace_colons_dim(V, indexes, 1, I)
541-
@inline replace_colons_dim(V, indexes, dim, I::Tuple{}) = ()
542-
@inline replace_colons_dim(V, indexes, dim, I::Tuple{Colon}) =
543-
(1:trailingsize(indexes[1], dim)*prod(index_lengths_dim(V.parent, length(V.indexes)-length(indexes)+2, tail(indexes)...)),)
544-
@inline replace_colons_dim(V, indexes, dim, I::Tuple{Colon, Vararg{Any}}) =
545-
(1:size(indexes[1], dim), replace_colons_dim(V, indexes, dim+1, tail(I))...)
546-
@inline replace_colons_dim(V, indexes, dim, I::Tuple{Any, Vararg{Any}}) =
547-
(I[1], replace_colons_dim(V, indexes, dim+1, tail(I))...)
548-
549-
550461
cumsum(A::AbstractArray, axis::Integer=1) = cumsum!(similar(A, Base._cumsum_type(A)), A, axis)
551462
cumsum!(B, A::AbstractArray) = cumsum!(B, A, 1)
552463
cumprod(A::AbstractArray, axis::Integer=1) = cumprod!(similar(A), A, axis)

base/reshapedarray.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ reshape(parent::AbstractArray, dims::Dims) = _reshape(parent, dims)
3838
reshape(parent::AbstractArray, len::Integer) = reshape(parent, (Int(len),))
3939
reshape(parent::AbstractArray, dims::Int...) = reshape(parent, dims)
4040

41+
reshape{T,N}(parent::AbstractArray{T,N}, ndims::Type{Val{N}}) = parent
4142
@generated function reshape{T,AN,N}(parent::AbstractArray{T,AN}, ndims::Type{Val{N}})
4243
if AN < N
4344
# Add trailing singleton dimensions

base/sharedarray.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ type SharedArray{T,N} <: DenseArray{T,N}
1919
# the local partition into the array when viewed as a single dimensional array.
2020
# this can be removed when @parallel or its equivalent supports looping on
2121
# a subset of workers.
22-
loc_subarr_1d::SubArray{T,1,Array{T,N},Tuple{UnitRange{Int}},true}
22+
loc_subarr_1d::SubArray{T,1,Array{T,1},Tuple{UnitRange{Int}},true}
2323

2424
SharedArray(d,p,r,sn) = new(d,p,r,sn)
2525
end

base/subarray.jl

Lines changed: 73 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -77,9 +77,23 @@ parentindexes(a::AbstractArray) = ntuple(i->1:size(a,i), ndims(a))
7777

7878
## SubArray creation
7979
# Drops singleton dimensions (those indexed with a scalar)
80-
function slice(A::AbstractArray, I::ViewIndex...)
80+
function slice{T,N}(A::AbstractArray{T,N}, I::Vararg{ViewIndex,N})
8181
@_inline_meta
8282
@boundscheck checkbounds(A, I...)
83+
unsafe_slice(A, I...)
84+
end
85+
function slice(A::AbstractArray, i::ViewIndex)
86+
@_inline_meta
87+
@boundscheck checkbounds(A, i)
88+
unsafe_slice(reshape(A, Val{1}), i)
89+
end
90+
function slice{N}(A::AbstractArray, I::Vararg{ViewIndex,N}) # TODO: DEPRECATE FOR #14770
91+
@_inline_meta
92+
@boundscheck checkbounds(A, I...)
93+
unsafe_slice(reshape(A, Val{N}), I...)
94+
end
95+
function unsafe_slice{T,N}(A::AbstractArray{T,N}, I::Vararg{ViewIndex,N})
96+
@_inline_meta
8397
J = to_indexes(I...)
8498
SubArray(A, J, index_shape(A, J...))
8599
end
@@ -89,9 +103,23 @@ keep_leading_scalars(T::Tuple{Real, Vararg{Real}}) = T
89103
keep_leading_scalars(T::Tuple{Real, Vararg{Any}}) = (@_inline_meta; (NoSlice(T[1]), keep_leading_scalars(tail(T))...))
90104
keep_leading_scalars(T::Tuple{Any, Vararg{Any}}) = (@_inline_meta; (T[1], keep_leading_scalars(tail(T))...))
91105

92-
function sub(A::AbstractArray, I::ViewIndex...)
106+
function sub{T,N}(A::AbstractArray{T,N}, I::Vararg{ViewIndex,N})
93107
@_inline_meta
94108
@boundscheck checkbounds(A, I...)
109+
unsafe_sub(A, I...)
110+
end
111+
function sub(A::AbstractArray, i::ViewIndex)
112+
@_inline_meta
113+
@boundscheck checkbounds(A, i)
114+
unsafe_sub(reshape(A, Val{1}), i)
115+
end
116+
function sub{N}(A::AbstractArray, I::Vararg{ViewIndex,N}) # TODO: DEPRECATE FOR #14770
117+
@_inline_meta
118+
@boundscheck checkbounds(A, I...)
119+
unsafe_sub(reshape(A, Val{N}), I...)
120+
end
121+
function unsafe_sub{T,N}(A::AbstractArray{T,N}, I::Vararg{ViewIndex,N})
122+
@_inline_meta
95123
J = keep_leading_scalars(to_indexes(I...))
96124
SubArray(A, J, index_shape(A, J...))
97125
end
@@ -100,90 +128,61 @@ end
100128
#
101129
# Recursively look through the heads of the parent- and sub-indexes, considering
102130
# the following cases:
103-
# * Parent index is empty -> ignore trailing scalars, but preserve added dimensions
104-
# * Parent index is Any -> re-index that with the sub-index
105-
# * Parent index is Scalar -> that dimension was dropped, so skip the sub-index and use the index as is
131+
# * Parent index is array -> re-index that with one or more sub-indexes (one per dimension)
132+
# * Parent index is Colon -> just use the sub-index as provided
133+
# * Parent index is scalar -> that dimension was dropped, so skip the sub-index and use the index as is
106134
#
107-
# Furthermore, we must specially consider the case with one final sub-index,
108-
# as it may be a linear index that spans multiple parent indexes.
109135

136+
typealias AbstractZeroDimArray{T} AbstractArray{T, 0}
110137
typealias DroppedScalar Union{Real, AbstractCartesianIndex}
111-
# When indexing beyond the parent indices, drop all trailing scalars (they must be 1 to be inbounds)
112-
reindex(V, idxs::Tuple{}, subidxs::Tuple{Vararg{DroppedScalar}}) = ()
113-
# Drop any intervening scalars that are beyond the parent indices but before a nonscalar
114-
reindex(V, idxs::Tuple{}, subidxs::Tuple{DroppedScalar, Vararg{Any}}) =
115-
(@_propagate_inbounds_meta; (reindex(V, idxs, tail(subidxs))...))
116-
# And keep the nonscalar index to add the dimension
117-
reindex(V, idxs::Tuple{}, subidxs::Tuple{Any, Vararg{Any}}) =
118-
(@_propagate_inbounds_meta; (subidxs[1], reindex(V, idxs, tail(subidxs))...))
138+
139+
reindex(V, ::Tuple{}, ::Tuple{}) = ()
119140

120141
# Skip dropped scalars, so simply peel them off the parent indices and continue
121142
reindex(V, idxs::Tuple{DroppedScalar, Vararg{Any}}, subidxs::Tuple{Vararg{Any}}) =
122143
(@_propagate_inbounds_meta; (idxs[1], reindex(V, tail(idxs), subidxs)...))
123144

124145
# Colons simply pass their subindexes straight through
125-
reindex(V, idxs::Tuple{Colon}, subidxs::Tuple{Any}) =
126-
(@_propagate_inbounds_meta; (subidxs[1],))
127146
reindex(V, idxs::Tuple{Colon, Vararg{Any}}, subidxs::Tuple{Any, Vararg{Any}}) =
128147
(@_propagate_inbounds_meta; (subidxs[1], reindex(V, tail(idxs), tail(subidxs))...))
129-
reindex(V, idxs::Tuple{Colon, Vararg{Any}}, subidxs::Tuple{Any}) =
130-
(@_propagate_inbounds_meta; (merge_indexes(V, idxs, subidxs[1]),))
131-
# As an optimization, we don't need to merge indices if all trailing indices are dropped scalars
132-
reindex(V, idxs::Tuple{Colon, Vararg{DroppedScalar}}, subidxs::Tuple{Any}) =
133-
(@_propagate_inbounds_meta; (subidxs[1], tail(idxs)...))
134148

135149
# Re-index into parent vectors with one subindex
136-
reindex(V, idxs::Tuple{AbstractVector}, subidxs::Tuple{Any}) =
137-
(@_propagate_inbounds_meta; (idxs[1][subidxs[1]],))
138150
reindex(V, idxs::Tuple{AbstractVector, Vararg{Any}}, subidxs::Tuple{Any, Vararg{Any}}) =
139151
(@_propagate_inbounds_meta; (idxs[1][subidxs[1]], reindex(V, tail(idxs), tail(subidxs))...))
140-
reindex(V, idxs::Tuple{AbstractVector, Vararg{Any}}, subidxs::Tuple{Any}) =
141-
(@_propagate_inbounds_meta; (merge_indexes(V, idxs, subidxs[1]),))
142-
reindex(V, idxs::Tuple{AbstractVector, Vararg{DroppedScalar}}, subidxs::Tuple{Any}) =
143-
(@_propagate_inbounds_meta; (idxs[1][subidxs[1]], tail(idxs)...))
144152

145153
# Parent matrices are re-indexed with two sub-indices
146-
reindex(V, idxs::Tuple{AbstractMatrix}, subidxs::Tuple{Any}) =
147-
(@_propagate_inbounds_meta; (idxs[1][subidxs[1]],))
148-
reindex(V, idxs::Tuple{AbstractMatrix}, subidxs::Tuple{Any, Any}) =
149-
(@_propagate_inbounds_meta; (idxs[1][subidxs[1], subidxs[2]],))
150154
reindex(V, idxs::Tuple{AbstractMatrix, Vararg{Any}}, subidxs::Tuple{Any, Any, Vararg{Any}}) =
151155
(@_propagate_inbounds_meta; (idxs[1][subidxs[1], subidxs[2]], reindex(V, tail(idxs), tail(tail(subidxs)))...))
152-
reindex(V, idxs::Tuple{AbstractMatrix, Vararg{Any}}, subidxs::Tuple{Any}) =
153-
(@_propagate_inbounds_meta; (merge_indexes(V, idxs, subidxs[1]),))
154-
reindex(V, idxs::Tuple{AbstractMatrix, Vararg{Any}}, subidxs::Tuple{Any, Any}) =
155-
(@_propagate_inbounds_meta; (merge_indexes(V, idxs, subidxs),))
156-
reindex(V, idxs::Tuple{AbstractMatrix, Vararg{DroppedScalar}}, subidxs::Tuple{Any}) =
157-
(@_propagate_inbounds_meta; (idxs[1][subidxs[1]], tail(idxs)...))
158-
reindex(V, idxs::Tuple{AbstractMatrix, Vararg{DroppedScalar}}, subidxs::Tuple{Any, Any}) =
159-
(@_propagate_inbounds_meta; (idxs[1][subidxs[1], subidxs[2]], tail(idxs)...))
160156

161157
# In general, we index N-dimensional parent arrays with N indices
162158
@generated function reindex{T,N}(V, idxs::Tuple{AbstractArray{T,N}, Vararg{Any}}, subidxs::Tuple{Vararg{Any}})
163159
if length(subidxs.parameters) >= N
164160
subs = [:(subidxs[$d]) for d in 1:N]
165161
tail = [:(subidxs[$d]) for d in N+1:length(subidxs.parameters)]
166162
:(@_propagate_inbounds_meta; (idxs[1][$(subs...)], reindex(V, tail(idxs), ($(tail...),))...))
167-
elseif length(idxs.parameters) == 1
168-
:(@_propagate_inbounds_meta; (idxs[1][subidxs...],))
169-
elseif all(T->T<:DroppedScalar, idxs.parameters[2:end])
170-
:(@_propagate_inbounds_meta; (idxs[1][subidxs...], tail(idxs)...))
171163
else
172-
:(@_propagate_inbounds_meta; (merge_indexes(V, idxs, subidxs),))
164+
:(error("mismatched index dimensionalities"))
173165
end
174166
end
175167

176168
# In general, we simply re-index the parent indices by the provided ones
177-
getindex(V::SubArray) = (@_propagate_inbounds_meta; getindex(V, 1))
178-
function getindex(V::SubArray, I::Real...)
169+
typealias SlowSubArray{T,N,P,I} SubArray{T,N,P,I,false}
170+
function getindex{T,N}(V::SlowSubArray{T,N}, I::Vararg{Real,N})
179171
@_inline_meta
180172
@boundscheck checkbounds(V, I...)
181173
@inbounds r = V.parent[reindex(V, V.indexes, to_indexes(I...))...]
182174
r
183175
end
176+
# Explicitly define scalar linear indexing -- this is needed so the nonscalar
177+
# indexing methods don't take precedence here
178+
function getindex(V::SlowSubArray, i::Real)
179+
@_inline_meta
180+
@boundscheck checkbounds(V, i)
181+
@inbounds r = V.parent[reindex(V, V.indexes, ind2sub(size(V), to_index(i)))...]
182+
r
183+
end
184184

185185
typealias FastSubArray{T,N,P,I} SubArray{T,N,P,I,true}
186-
getindex(V::FastSubArray) = (@_propagate_inbounds_meta; getindex(V, 1))
187186
function getindex(V::FastSubArray, i::Real)
188187
@_inline_meta
189188
@boundscheck checkbounds(V, i)
@@ -198,40 +197,55 @@ function getindex(V::FastContiguousSubArray, i::Real)
198197
@inbounds r = V.parent[V.first_index + to_index(i)-1]
199198
r
200199
end
201-
# We need this because the ::ViewIndex... method would otherwise obscure the Base fallback
202-
function getindex(V::FastSubArray, I::Real...)
200+
# Just like the slow case, explicitly define scalar indexing at N dims, too
201+
function getindex{T,N}(V::FastSubArray{T,N}, I::Vararg{Real,N})
203202
@_inline_meta
204203
@boundscheck checkbounds(V, I...)
205204
@inbounds r = getindex(V, sub2ind(size(V), to_indexes(I...)...))
206205
r
207206
end
208-
getindex{T,N}(V::SubArray{T,N}, I::ViewIndex...) = (@_propagate_inbounds_meta; copy(slice(V, I...)))
209207

210-
setindex!(V::SubArray, x) = (@_propagate_inbounds_meta; setindex!(V, x, 1))
211-
function setindex!{T,N}(V::SubArray{T,N}, x, I::Real...)
208+
# Nonscalar indexing just copies a view
209+
getindex{T,N}(V::SubArray{T,N}, i::ViewIndex) = (@_propagate_inbounds_meta; copy(slice(V, i)))
210+
getindex{T,N}(V::SubArray{T,N}, I::Vararg{ViewIndex,N}) = (@_propagate_inbounds_meta; copy(slice(V, I...)))
211+
212+
# Setindex is similar, but since we don't specially define non-scalar methods
213+
# we only need to define the canonical methods. We don't need to worry about
214+
# e.g., linear indexing for SlowSubArray since the fallbacks can do their thing
215+
function setindex!{T,N}(V::SlowSubArray{T,N}, x, I::Vararg{Real,N})
212216
@_inline_meta
213217
@boundscheck checkbounds(V, I...)
214218
@inbounds V.parent[reindex(V, V.indexes, to_indexes(I...))...] = x
215219
V
216220
end
221+
function setindex!(V::FastSubArray, x, i::Real)
222+
@_inline_meta
223+
@boundscheck checkbounds(V, i)
224+
@inbounds V.parent[V.first_index + V.stride1*(to_index(i)-1)] = x
225+
V
226+
end
227+
function setindex!(V::FastContiguousSubArray, x, i::Real)
228+
@_inline_meta
229+
@boundscheck checkbounds(V, i)
230+
@inbounds V.parent[V.first_index + to_index(i)-1] = x
231+
V
232+
end
217233
# Nonscalar setindex! falls back to the defaults
218234

219-
function slice{T,N}(V::SubArray{T,N}, I::ViewIndex...)
235+
function unsafe_slice{T,N}(V::SubArray{T,N}, I::Vararg{ViewIndex,N})
220236
@_inline_meta
221-
@boundscheck checkbounds(V, I...)
222237
idxs = reindex(V, V.indexes, to_indexes(I...))
223238
SubArray(V.parent, idxs, index_shape(V.parent, idxs...))
224239
end
225240

226-
function sub{T,N}(V::SubArray{T,N}, I::ViewIndex...)
241+
function unsafe_sub{T,N}(V::SubArray{T,N}, I::Vararg{ViewIndex,N})
227242
@_inline_meta
228-
@boundscheck checkbounds(V, I...)
229243
idxs = reindex(V, V.indexes, keep_leading_scalars(to_indexes(I...)))
230244
SubArray(V.parent, idxs, index_shape(V.parent, idxs...))
231245
end
232246

233-
linearindexing(A::FastSubArray) = LinearFast()
234-
linearindexing(A::SubArray) = LinearSlow()
247+
linearindexing{T<:FastSubArray}(::Type{T}) = LinearFast()
248+
linearindexing{T<:SubArray}(::Type{T}) = LinearSlow()
235249

236250
getindex(::Colon, i) = to_index(i)
237251
unsafe_getindex(::Colon, i) = to_index(i)

0 commit comments

Comments
 (0)