diff --git a/base/abstractarray.jl b/base/abstractarray.jl index 62bf0901a0507..6cfebcb0ad75c 100644 --- a/base/abstractarray.jl +++ b/base/abstractarray.jl @@ -1076,7 +1076,10 @@ function copyto!(deststyle::IndexStyle, dest::AbstractArray, srcstyle::IndexStyl copyto_unaliased!(deststyle, dest, srcstyle, src′) end -function copyto_unaliased!(deststyle::IndexStyle, dest::AbstractArray, srcstyle::IndexStyle, src::AbstractArray) +function copyto_unaliased!(::IndexLinear, dest::AbstractArray, ::IndexLinear, src::AbstractArray) + copyto!(dest, first(LinearIndices(dest)), src, first(LinearIndices(src)), length(src)) +end +function copyto_unaliased!(deststyle::IndexStyle, dest::AbstractArray, ::IndexStyle, src::AbstractArray) isempty(src) && return dest destinds, srcinds = LinearIndices(dest), LinearIndices(src) idf, isf = first(destinds), first(srcinds) @@ -1084,17 +1087,11 @@ function copyto_unaliased!(deststyle::IndexStyle, dest::AbstractArray, srcstyle: (checkbounds(Bool, destinds, isf+Δi) & checkbounds(Bool, destinds, last(srcinds)+Δi)) || throw(BoundsError(dest, srcinds)) if deststyle isa IndexLinear - if srcstyle isa IndexLinear - # Single-index implementation - @inbounds for i in srcinds - dest[i + Δi] = src[i] - end - else - # Dual-index implementation - i = idf - 1 - @inbounds for a in src - dest[i+=1] = a - end + # IndexStyle(src) is IndexCartesian, as the linear indexing case is handled separately + # Dual-index implementation + i = idf - 1 + @inbounds for a in src + dest[i+=1] = a end else iterdest, itersrc = eachindex(dest), eachindex(src) @@ -1123,7 +1120,28 @@ function copyto!(dest::AbstractArray, dstart::Integer, src::AbstractArray, sstar copyto!(dest, dstart, src, sstart, last(srcinds)-sstart+1) end +# we add an extra level of indirection to forward the copy +# to the parent for contiguous linear views function copyto!(dest::AbstractArray, dstart::Integer, + src::AbstractArray, sstart::Integer, n::Integer) + # check if the arrays are views that may be unwrapped + # if yes, try to use the copyto! implementation for the parents + # if no, then fall back to the default implementation that loops over the arrays + __copyto!(dest, _unwrap_view(dest, dstart)..., src, _unwrap_view(src, sstart)..., n) +end +# `_unwrap_view` potentially unwraps a SubArray and shifts the index `ind` by the linear offset of the view +# If the array `A` is not a view, there's nothing to unwrap +_unwrap_view(A, ind) = A, ind +# fallback method if neither array is a SubArray, in which case we loop over them +function __copyto!(dest::A, ::A, dstart, src::B, ::B, sstart, n) where {A,B} + _copyto!(dest, dstart, src, sstart, n) +end +# Forward the copy to the parent if there is any contiguous, linearly indexed view +function __copyto!(_, destp, dstart, _, srcp, sstart, n) + copyto!(destp, dstart, srcp, sstart, n) +end + +function _copyto!(dest::AbstractArray, dstart::Integer, src::AbstractArray, sstart::Integer, n::Integer) n == 0 && return dest diff --git a/base/sort.jl b/base/sort.jl index 8254f56b3f952..b5af265f6dad9 100644 --- a/base/sort.jl +++ b/base/sort.jl @@ -1955,12 +1955,12 @@ function sortperm(A::AbstractArray; scratch::Union{Vector{<:Integer}, Nothing}=nothing, dims...) #to optionally specify dims argument if rev === true - _sortperm(A; alg, order=ord(lt, by, true, order), scratch, dims...) + _sortperm(A, alg, ord(lt, by, true, order), scratch, dims) else - _sortperm(A; alg, order=ord(lt, by, nothing, order), scratch, dims...) + _sortperm(A, alg, ord(lt, by, nothing, order), scratch, dims) end end -function _sortperm(A::AbstractArray; alg, order, scratch, dims...) +function _sortperm(A::AbstractArray, alg, order, scratch, dims) if order === Forward && isa(A,Vector) && eltype(A)<:Integer n = length(A) if n > 1 diff --git a/base/subarray.jl b/base/subarray.jl index eacaddc068f1f..caddaa9f2be4e 100644 --- a/base/subarray.jl +++ b/base/subarray.jl @@ -348,6 +348,10 @@ FastContiguousSubArray{T,N,P,I<:Union{Tuple{AbstractUnitRange, Vararg{Any}}, @inline _reindexlinear(V::FastContiguousSubArray, i::Int) = V.offset1 + i @inline _reindexlinear(V::FastContiguousSubArray, i::AbstractUnitRange{Int}) = V.offset1 .+ i +# we may forward the 5-term copyto! for contiguous linearly indexed views to the corresponding parents +# this lets us access optimized copyto! implementations for the parent +_unwrap_view(A::FastContiguousSubArray, Astart) = parent(A), A.offset1 + Astart + """ An internal type representing arrays stored contiguously in memory. """ diff --git a/test/subarray.jl b/test/subarray.jl index a462224e7643a..746bdc71a0051 100644 --- a/test/subarray.jl +++ b/test/subarray.jl @@ -1098,3 +1098,28 @@ end @test Base.mightalias(permutedims(V1), V1) @test Base.mightalias(permutedims(V1), permutedims(V1)) end + +@testset "copyto! with linearly indexed views" begin + @testset "FastContiguousSubArray" begin + A = rand(4) + vA = view(A, :) + B = zeros(4) + vB = view(B, :) + for y in (B, vB), x in (A, vA) + y .= 0 + copyto!(y, x) + @test y == x + end + end + @testset "FastSubArray" begin + A = rand(4) + vA = @view A[1:2:end] + B = zeros(4) + vB = @view B[1:2:end] + @test copyto!(vB, vA) == vA + B .= 0 + @test copyto!(B, vA)[axes(vA,1)] == vA + B .= 0 + @test copyto!(vB, 1, A, 1, 2) == view(A, 1:2) + end +end