diff --git a/src/blockaxis.jl b/src/blockaxis.jl index 649cb3fd..57d6f014 100644 --- a/src/blockaxis.jl +++ b/src/blockaxis.jl @@ -7,10 +7,16 @@ @propagate_inbounds getindex(b::AbstractArray, K::BlockIndex{1}, J::BlockIndex{1}...) = b[BlockIndex(tuple(K, J...))] -@propagate_inbounds getindex(b::AbstractArray{T,N}, K::BlockIndices{N}) where {T,N} = b[block(K)][K.indices...] @propagate_inbounds getindex(b::LayoutArray{T,N}, K::BlockIndices{N}) where {T,N} = b[block(K)][K.indices...] @propagate_inbounds getindex(b::LayoutArray{T,1}, K::BlockIndices{1}) where {T} = b[block(K)][K.indices...] +# Narrow method for non-blocked unit ranges (e.g. Base.OneTo, UnitRange). +# Unlike getindex(::AbstractArray, ::BlockIndices) which caused 12k+ invalidations, +# AbstractUnitRange{<:Integer} is narrow enough to avoid mass invalidation. +# AbstractBlockedUnitRange has its own more-specific method (below), so this only +# handles plain ranges where block(K) is always Block(1). +@propagate_inbounds getindex(b::AbstractUnitRange{<:Integer}, K::BlockIndices{1}) = b[block(K)][K.indices...] + function findblockindex(b::AbstractVector, k::Integer) @boundscheck k in b || throw(BoundsError()) bl = blocklasts(b) diff --git a/src/views.jl b/src/views.jl index 702e2f9e..91e6c3ba 100644 --- a/src/views.jl +++ b/src/views.jl @@ -18,6 +18,23 @@ _blockslice(B, a) = NoncontiguousBlockSlice(B, a) # Need to check the length of I in case its empty unblock(A, ::Tuple{}, I) = BlockSlice(first(I),Base.OneTo(length(I[1]))) +# For non-blocked axes (e.g. Base.OneTo), decompose BlockIndices into +# block-level indexing + sub-indexing to avoid the need for +# getindex(::AbstractArray, ::BlockIndices) which causes invalidations. +# Block-level indexing on AbstractUnitRange is handled by +# getindex(::AbstractUnitRange{<:Integer}, ::Block{1}) which returns the range. +@inline function unblock(A, inds::Tuple{AbstractUnitRange{<:Integer}, Vararg}, I::Tuple{BlockIndices{1}, Vararg}) + bir = first(I) + block_range = inds[1][block(bir)] + _blockslice(bir, block_range[bir.indices...]) +end +# AbstractBlockedUnitRange has its own getindex(::AbstractBlockedUnitRange, ::BlockIndices{1}), +# so use the default unblock behavior (index axis directly with BlockIndices). +@inline function unblock(A, inds::Tuple{AbstractBlockedUnitRange, Vararg}, I::Tuple{BlockIndices{1}, Vararg}) + B = first(I) + _blockslice(B, inds[1][B]) +end + to_index(::Block) = throw(ArgumentError("Block must be converted by to_indices(...)")) to_index(::BlockIndex) = throw(ArgumentError("BlockIndex must be converted by to_indices(...)")) to_index(::BlockIndices) = throw(ArgumentError("BlockIndices must be converted by to_indices(...)"))