Skip to content

Commit f09dff8

Browse files
jishnubtecosaur
authored andcommitted
Add _unsetindex! methods for SubArrays and CartesianIndexes (JuliaLang#53383)
With this, the following (and equivalent calls) work: ```julia julia> copyto!(view(zeros(BigInt, 2), 1:2), Vector{BigInt}(undef,2)) 2-element view(::Vector{BigInt}, 1:2) with eltype BigInt: #undef #undef julia> copyto!(view(zeros(BigInt, 2), 1:2), view(Vector{BigInt}(undef,2), 1:2)) 2-element view(::Vector{BigInt}, 1:2) with eltype BigInt: #undef #undef ``` Close JuliaLang#53098. With this, all the `_unsetindex!` branches in `copyto_unaliased!` work for `Array`-views, and this makes certain indexing operations vectorize and speed-up: ```julia julia> using BenchmarkTools julia> a = view(rand(100,100), 1:100, 1:100); b = view(similar(a), axes(a)...); julia> @Btime copyto!($b, $a); 16.427 μs (0 allocations: 0 bytes) # master 2.308 μs (0 allocations: 0 bytes) # PR ``` Improves (but doesn't resolve) JuliaLang#40962 and JuliaLang#53158 ```julia julia> a = rand(40,40); b = rand(40,40); julia> @Btime $a[1:end,1:end] .= $b; 5.383 μs (0 allocations: 0 bytes) # v"1.12.0-DEV.16" 3.194 μs (0 allocations: 0 bytes) # PR ``` ƒ Co-authored-by: Jameson Nash <[email protected]>
1 parent 499d13e commit f09dff8

File tree

6 files changed

+118
-2
lines changed

6 files changed

+118
-2
lines changed

base/abstractarray.jl

+14-1
Original file line numberDiff line numberDiff line change
@@ -1472,7 +1472,20 @@ function _setindex!(::IndexCartesian, A::AbstractArray, v, I::Vararg{Int,M}) whe
14721472
r
14731473
end
14741474

1475-
_unsetindex!(A::AbstractArray, i::Integer) = _unsetindex!(A, to_index(i))
1475+
function _unsetindex!(A::AbstractArray, i::Integer...)
1476+
@_propagate_inbounds_meta
1477+
_unsetindex!(A, map(to_index, i)...)
1478+
end
1479+
1480+
function _unsetindex!(A::AbstractArray{T}, i::Int...) where T
1481+
# this provides a fallback method which is a no-op if the element is already unassigned
1482+
# such that copying into an uninitialized object generally always will work,
1483+
# even if the specific custom array type has not implemented `_unsetindex!`
1484+
@inline
1485+
@boundscheck checkbounds(A, i...)
1486+
allocatedinline(T) || @inbounds(!isassigned(A, i...)) || throw(MethodError(_unsetindex!, (A, i...)))
1487+
return A
1488+
end
14761489

14771490
"""
14781491
parent(A)

base/array.jl

+6-1
Original file line numberDiff line numberDiff line change
@@ -219,7 +219,12 @@ function _unsetindex!(A::Array, i::Int)
219219
@inbounds _unsetindex!(GenericMemoryRef(A.ref, i))
220220
return A
221221
end
222-
222+
function _unsetindex!(A::Array, i::Int...)
223+
@inline
224+
@boundscheck checkbounds(A, i...)
225+
@inbounds _unsetindex!(A, _to_linear_index(A, i...))
226+
return A
227+
end
223228

224229
# TODO: deprecate this (aligned_sizeof and/or elsize and/or sizeof(Some{T}) are more correct)
225230
elsize(::Type{A}) where {T,A<:Array{T}} = aligned_sizeof(T)

base/multidimensional.jl

+6
Original file line numberDiff line numberDiff line change
@@ -1610,6 +1610,12 @@ end
16101610
end
16111611
end
16121612

1613+
# _unsetindex
1614+
@propagate_inbounds function Base._unsetindex!(A::AbstractArray, i::CartesianIndex)
1615+
Base._unsetindex!(A, to_indices(A, (i,))...)
1616+
return A
1617+
end
1618+
16131619
## permutedims
16141620

16151621
## Permute array dims ##

base/subarray.jl

+19
Original file line numberDiff line numberDiff line change
@@ -410,6 +410,25 @@ function isassigned(V::FastSubArray{<:Any, 1}, i::Int)
410410
r
411411
end
412412

413+
function _unsetindex!(V::FastSubArray, i::Int)
414+
@inline
415+
@boundscheck checkbounds(Bool, V, i)
416+
@inbounds _unsetindex!(V.parent, _reindexlinear(V, i))
417+
return V
418+
end
419+
function _unsetindex!(V::FastSubArray{<:Any,1}, i::Int)
420+
@inline
421+
@boundscheck checkbounds(Bool, V, i)
422+
@inbounds _unsetindex!(V.parent, _reindexlinear(V, i))
423+
return V
424+
end
425+
function _unsetindex!(V::SubArray{T,N}, i::Vararg{Int,N}) where {T,N}
426+
@inline
427+
@boundscheck checkbounds(Bool, V, i...)
428+
@inbounds _unsetindex!(V.parent, reindex(V.indices, i)...)
429+
return V
430+
end
431+
413432
IndexStyle(::Type{<:FastSubArray}) = IndexLinear()
414433
IndexStyle(::Type{<:SubArray}) = IndexCartesian()
415434

test/abstractarray.jl

+31
Original file line numberDiff line numberDiff line change
@@ -2019,3 +2019,34 @@ end
20192019
@test B == A
20202020
end
20212021
end
2022+
2023+
@testset "_unsetindex!" begin
2024+
struct MyMatrixUnsetIndexCartInds{T,A<:AbstractMatrix{T}} <: AbstractMatrix{T}
2025+
data :: A
2026+
end
2027+
Base.size(A::MyMatrixUnsetIndexCartInds) = size(A.data)
2028+
Base.getindex(M::MyMatrixUnsetIndexCartInds, i::Int, j::Int) = M.data[i,j]
2029+
Base.setindex!(M::MyMatrixUnsetIndexCartInds, v, i::Int, j::Int) = setindex!(M.data, v, i, j)
2030+
struct MyMatrixUnsetIndexLinInds{T,A<:AbstractMatrix{T}} <: AbstractMatrix{T}
2031+
data :: A
2032+
end
2033+
Base.size(A::MyMatrixUnsetIndexLinInds) = size(A.data)
2034+
Base.getindex(M::MyMatrixUnsetIndexLinInds, i::Int) = M.data[i]
2035+
Base.setindex!(M::MyMatrixUnsetIndexLinInds, v, i::Int) = setindex!(M.data, v, i)
2036+
Base.IndexStyle(::Type{<:MyMatrixUnsetIndexLinInds}) = IndexLinear()
2037+
2038+
function test_unsetindex(MT)
2039+
M = MT(ones(2,2))
2040+
M2 = MT(Matrix{BigFloat}(undef, 2,2))
2041+
copyto!(M, M2)
2042+
@test all(==(1), M)
2043+
M3 = MT(Matrix{BigFloat}(undef, 2,2))
2044+
for i in eachindex(M3)
2045+
@test !isassigned(M3, i)
2046+
end
2047+
M3 .= 1
2048+
@test_throws MethodError copyto!(M3, M2)
2049+
end
2050+
test_unsetindex(MyMatrixUnsetIndexCartInds)
2051+
test_unsetindex(MyMatrixUnsetIndexLinInds)
2052+
end

test/subarray.jl

+42
Original file line numberDiff line numberDiff line change
@@ -1055,4 +1055,46 @@ end
10551055
@test !isassigned(v, 1, 2) # inbounds but not assigned
10561056
@test !isassigned(v, 3, 3) # out-of-bounds
10571057
end
1058+
1059+
@testset "_unsetindex!" begin
1060+
function test_unsetindex(A, B)
1061+
copyto!(A, B)
1062+
for i in eachindex(A)
1063+
@test !isassigned(A, i)
1064+
end
1065+
end
1066+
@testset "dest IndexLinear, src IndexLinear" begin
1067+
for p in (fill(BigInt(2)), BigInt[1, 2], BigInt[1 2; 3 4])
1068+
A = view(copy(p), ntuple(_->:, ndims(p))...)
1069+
B = view(similar(A), ntuple(_->:, ndims(p))...)
1070+
test_unsetindex(A, B)
1071+
test_unsetindex(p, B)
1072+
end
1073+
end
1074+
1075+
@testset "dest IndexLinear, src IndexCartesian" begin
1076+
for p in (fill(BigInt(2)), BigInt[1, 2], BigInt[1 2; 3 4])
1077+
A = view(copy(p), ntuple(_->:, ndims(p))...)
1078+
B = view(similar(A), axes(A)...)
1079+
test_unsetindex(A, B)
1080+
test_unsetindex(p, B)
1081+
end
1082+
end
1083+
1084+
@testset "dest IndexCartesian, src IndexLinear" begin
1085+
for p in (fill(BigInt(2)), BigInt[1, 2], BigInt[1 2; 3 4])
1086+
A = view(p, axes(p)...)
1087+
B = similar(A)
1088+
test_unsetindex(A, B)
1089+
end
1090+
end
1091+
1092+
@testset "dest IndexCartesian, src IndexCartesian" begin
1093+
for p in (fill(BigInt(2)), BigInt[1, 2], BigInt[1 2; 3 4])
1094+
A = view(p, axes(p)...)
1095+
B = view(similar(A), axes(A)...)
1096+
test_unsetindex(A, B)
1097+
end
1098+
end
1099+
end
10581100
end

0 commit comments

Comments
 (0)