Skip to content

Commit 0ce871d

Browse files
committed
Unwrap contiguous views in copyto!
1 parent c93e28e commit 0ce871d

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
@@ -1076,25 +1076,23 @@ function copyto!(deststyle::IndexStyle, dest::AbstractArray, srcstyle::IndexStyl
10761076
copyto_unaliased!(deststyle, dest, srcstyle, src′)
10771077
end
10781078

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

1124+
# we add an extra level of indirection to forward the copy
1125+
# to the parent for contiguous linear views
11261126
function copyto!(dest::AbstractArray, dstart::Integer,
1127+
src::AbstractArray, sstart::Integer, n::Integer)
1128+
@_propagate_inbounds_meta
1129+
# check if the arrays are views that may be unwrapped
1130+
# if yes, try to use the copyto! implementation for the parents
1131+
# if no, then fall back to the default implementation that loops over the arrays
1132+
__copyto!(dest, _unwrap_view(dest, dstart)..., src, _unwrap_view(src, sstart)..., n)
1133+
end
1134+
# if the array A is not a view, there's nothig to unwrap
1135+
_unwrap_view(A, ind) = A, ind
1136+
# fallback method if the arrays aren't views, in which case there's nothing to unwrap
1137+
function __copyto!(dest::A, ::A, dstart, src::B, ::B, sstart, n) where {A,B}
1138+
@_propagate_inbounds_meta
1139+
_copyto!(dest, dstart, src, sstart, n)
1140+
end
1141+
# try copying to the parents if there is at least one contiguous view
1142+
function __copyto!(_, destp, dstart, _, srcp, sstart, n)
1143+
@_propagate_inbounds_meta
1144+
copyto!(destp, dstart, srcp, sstart, n)
1145+
end
1146+
1147+
function _copyto!(dest::AbstractArray, dstart::Integer,
11271148
src::AbstractArray, sstart::Integer,
11281149
n::Integer)
11291150
n == 0 && return dest
11301151
n < 0 && throw(ArgumentError(LazyString("tried to copy n=",
11311152
n," elements, but n should be non-negative")))
11321153
destinds, srcinds = LinearIndices(dest), LinearIndices(src)
1133-
(checkbounds(Bool, destinds, dstart) && checkbounds(Bool, destinds, dstart+n-1)) || throw(BoundsError(dest, dstart:dstart+n-1))
1134-
(checkbounds(Bool, srcinds, sstart) && checkbounds(Bool, srcinds, sstart+n-1)) || throw(BoundsError(src, sstart:sstart+n-1))
1154+
@boundscheck begin
1155+
(checkbounds(Bool, destinds, dstart) && checkbounds(Bool, destinds, dstart+n-1)) || throw(BoundsError(dest, dstart:dstart+n-1))
1156+
(checkbounds(Bool, srcinds, sstart) && checkbounds(Bool, srcinds, sstart+n-1)) || throw(BoundsError(src, sstart:sstart+n-1))
1157+
end
11351158
src′ = unalias(dest, src)
11361159
@inbounds for i = 0:n-1
11371160
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
@@ -1098,3 +1098,28 @@ end
10981098
@test Base.mightalias(permutedims(V1), V1)
10991099
@test Base.mightalias(permutedims(V1), permutedims(V1))
11001100
end
1101+
1102+
@testset "copyto! with linearly indexed views" begin
1103+
@testset "FastContiguousSubArray" begin
1104+
A = rand(4)
1105+
vA = view(A, :)
1106+
B = zeros(4)
1107+
vB = view(B, :)
1108+
for y in (B, vB), x in (A, vA)
1109+
y .= 0
1110+
copyto!(y, x)
1111+
@test y == x
1112+
end
1113+
end
1114+
@testset "FastSubArray" begin
1115+
A = rand(4)
1116+
vA = @view A[1:2:end]
1117+
B = zeros(4)
1118+
vB = @view B[1:2:end]
1119+
@test copyto!(vB, vA) == vA
1120+
B .= 0
1121+
@test copyto!(B, vA)[axes(vA,1)] == vA
1122+
B .= 0
1123+
@test copyto!(vB, 1, A, 1, 2) == view(A, 1:2)
1124+
end
1125+
end

0 commit comments

Comments
 (0)