Skip to content

Commit 9bbe697

Browse files
committed
Unwrap contiguous views in copyto!
1 parent 0ded536 commit 9bbe697

File tree

3 files changed

+67
-15
lines changed

3 files changed

+67
-15
lines changed

base/abstractarray.jl

Lines changed: 38 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1077,25 +1077,23 @@ function copyto!(deststyle::IndexStyle, dest::AbstractArray, srcstyle::IndexStyl
10771077
copyto_unaliased!(deststyle, dest, srcstyle, src′)
10781078
end
10791079

1080-
function copyto_unaliased!(deststyle::IndexStyle, dest::AbstractArray, srcstyle::IndexStyle, src::AbstractArray)
1080+
function copyto_unaliased!(::IndexLinear, dest::AbstractArray, ::IndexLinear, src::AbstractArray)
1081+
@_propagate_inbounds_meta
1082+
copyto!(dest, firstindex(dest), src, firstindex(src), length(src))
1083+
end
1084+
function copyto_unaliased!(deststyle::IndexStyle, dest::AbstractArray, ::IndexStyle, src::AbstractArray)
10811085
isempty(src) && return dest
10821086
destinds, srcinds = LinearIndices(dest), LinearIndices(src)
10831087
idf, isf = first(destinds), first(srcinds)
10841088
Δi = idf - isf
1085-
(checkbounds(Bool, destinds, isf+Δi) & checkbounds(Bool, destinds, last(srcinds)+Δi)) ||
1089+
@boundscheck (checkbounds(Bool, destinds, isf+Δi) & checkbounds(Bool, destinds, last(srcinds)+Δi)) ||
10861090
throw(BoundsError(dest, srcinds))
10871091
if deststyle isa IndexLinear
1088-
if srcstyle isa IndexLinear
1089-
# Single-index implementation
1090-
@inbounds for i in srcinds
1091-
dest[i + Δi] = src[i]
1092-
end
1093-
else
1094-
# Dual-index implementation
1095-
i = idf - 1
1096-
@inbounds for a in src
1097-
dest[i+=1] = a
1098-
end
1092+
# IndexStyle(src) is IndexCartesian, as the linear indexing case is handled separately
1093+
# Dual-index implementation
1094+
i = idf - 1
1095+
@inbounds for a in src
1096+
dest[i+=1] = a
10991097
end
11001098
else
11011099
iterdest, itersrc = eachindex(dest), eachindex(src)
@@ -1124,15 +1122,40 @@ function copyto!(dest::AbstractArray, dstart::Integer, src::AbstractArray, sstar
11241122
copyto!(dest, dstart, src, sstart, last(srcinds)-sstart+1)
11251123
end
11261124

1125+
# we add an extra level of indirection to forward the copy
1126+
# to the parent for contiguous linear views
11271127
function copyto!(dest::AbstractArray, dstart::Integer,
1128+
src::AbstractArray, sstart::Integer, n::Integer)
1129+
@_propagate_inbounds_meta
1130+
# check if the arrays are views that may be unwrapped
1131+
# if yes, try to use the copyto! implementation for the parents
1132+
# if no, then fall back to the default implementation that loops over the arrays
1133+
__copyto!(dest, _unwrap_view(dest, dstart)..., src, _unwrap_view(src, sstart)..., n)
1134+
end
1135+
# if the array A is not a view, there's nothig to unwrap
1136+
_unwrap_view(A, ind) = A, ind
1137+
# fallback method if the arrays aren't views, in which case there's nothing to unwrap
1138+
function __copyto!(dest::A, ::A, dstart, src::B, ::B, sstart, n) where {A,B}
1139+
@_propagate_inbounds_meta
1140+
_copyto!(dest, dstart, src, sstart, n)
1141+
end
1142+
# try copying to the parents if there is at least one contiguous view
1143+
function __copyto!(_, destp, dstart, _, srcp, sstart, n)
1144+
@_propagate_inbounds_meta
1145+
copyto!(destp, dstart, srcp, sstart, n)
1146+
end
1147+
1148+
function _copyto!(dest::AbstractArray, dstart::Integer,
11281149
src::AbstractArray, sstart::Integer,
11291150
n::Integer)
11301151
n == 0 && return dest
11311152
n < 0 && throw(ArgumentError(LazyString("tried to copy n=",
11321153
n," elements, but n should be non-negative")))
11331154
destinds, srcinds = LinearIndices(dest), LinearIndices(src)
1134-
(checkbounds(Bool, destinds, dstart) && checkbounds(Bool, destinds, dstart+n-1)) || throw(BoundsError(dest, dstart:dstart+n-1))
1135-
(checkbounds(Bool, srcinds, sstart) && checkbounds(Bool, srcinds, sstart+n-1)) || throw(BoundsError(src, sstart:sstart+n-1))
1155+
@boundscheck begin
1156+
(checkbounds(Bool, destinds, dstart) && checkbounds(Bool, destinds, dstart+n-1)) || throw(BoundsError(dest, dstart:dstart+n-1))
1157+
(checkbounds(Bool, srcinds, sstart) && checkbounds(Bool, srcinds, sstart+n-1)) || throw(BoundsError(src, sstart:sstart+n-1))
1158+
end
11361159
src′ = unalias(dest, src)
11371160
@inbounds for i = 0:n-1
11381161
dest[dstart+i] = src′[sstart+i]

base/subarray.jl

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -348,6 +348,10 @@ FastContiguousSubArray{T,N,P,I<:Union{Tuple{AbstractUnitRange, Vararg{Any}},
348348
@inline _reindexlinear(V::FastContiguousSubArray, i::Int) = V.offset1 + i
349349
@inline _reindexlinear(V::FastContiguousSubArray, i::AbstractUnitRange{Int}) = V.offset1 .+ i
350350

351+
# we may forward the 5-term copyto! for contiguous linearly indexed views to the corresponding parents
352+
# this lets us access optimized copyto! implementations for the parent
353+
_unwrap_view(A::FastContiguousSubArray, Astart) = parent(A), A.offset1 + Astart
354+
351355
"""
352356
An internal type representing arrays stored contiguously in memory.
353357
"""

test/subarray.jl

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1123,3 +1123,28 @@ end
11231123
@test Base.mightalias(permutedims(V1), V1)
11241124
@test Base.mightalias(permutedims(V1), permutedims(V1))
11251125
end
1126+
1127+
@testset "copyto! with linearly indexed views" begin
1128+
@testset "FastContiguousSubArray" begin
1129+
A = rand(4)
1130+
vA = view(A, :)
1131+
B = zeros(4)
1132+
vB = view(B, :)
1133+
for y in (B, vB), x in (A, vA)
1134+
y .= 0
1135+
copyto!(y, x)
1136+
@test y == x
1137+
end
1138+
end
1139+
@testset "FastSubArray" begin
1140+
A = rand(4)
1141+
vA = @view A[1:2:end]
1142+
B = zeros(4)
1143+
vB = @view B[1:2:end]
1144+
@test copyto!(vB, vA) == vA
1145+
B .= 0
1146+
@test copyto!(B, vA)[axes(vA,1)] == vA
1147+
B .= 0
1148+
@test copyto!(vB, 1, A, 1, 2) == view(A, 1:2)
1149+
end
1150+
end

0 commit comments

Comments
 (0)