1
1
# host-level indexing
2
2
3
3
4
- # basic indexing with integers
4
+ # indexing operators
5
5
6
6
Base. IndexStyle (:: Type{<:AbstractGPUArray} ) = Base. IndexLinear ()
7
7
8
- function Base. getindex (xs:: AbstractGPUArray{T} , I:: Integer... ) where T
8
+ vectorized_indices (Is:: Union{Integer,CartesianIndex} ...) = Val {false} ()
9
+ vectorized_indices (Is... ) = Val {true} ()
10
+
11
+ # TODO : re-use Base functionality for the conversion of indices to a linear index,
12
+ # by only implementing `getindex(A, ::Int)` etc. this is difficult due to
13
+ # ambiguities with the vectorized method that can take any index type.
14
+
15
+ Base. @propagate_inbounds Base. getindex (A:: AbstractGPUArray , Is... ) =
16
+ _getindex (vectorized_indices (Is... ), A, to_indices (A, Is)... )
17
+ Base. @propagate_inbounds _getindex (:: Val{false} , A:: AbstractGPUArray , Is... ) =
18
+ scalar_getindex (A, to_indices (A, Is)... )
19
+ Base. @propagate_inbounds _getindex (:: Val{true} , A:: AbstractGPUArray , Is... ) =
20
+ vectorized_getindex (A, to_indices (A, Is)... )
21
+
22
+ Base. @propagate_inbounds Base. setindex! (A:: AbstractGPUArray , v, Is... ) =
23
+ _setindex! (vectorized_indices (Is... ), A, v, to_indices (A, Is)... )
24
+ Base. @propagate_inbounds _setindex! (:: Val{false} , A:: AbstractGPUArray , v, Is... ) =
25
+ scalar_setindex! (A, v, to_indices (A, Is)... )
26
+ Base. @propagate_inbounds _setindex! (:: Val{true} , A:: AbstractGPUArray , v, Is... ) =
27
+ vectorized_setindex! (A, v, to_indices (A, Is)... )
28
+
29
+ # # scalar indexing
30
+
31
+ function scalar_getindex (A:: AbstractGPUArray{T} , Is... ) where T
32
+ @boundscheck checkbounds (A, Is... )
33
+ I = Base. _to_linear_index (A, Is... )
34
+ getindex (A, I)
35
+ end
36
+
37
+ function scalar_setindex! (A:: AbstractGPUArray{T} , v, Is... ) where T
38
+ @boundscheck checkbounds (A, Is... )
39
+ I = Base. _to_linear_index (A, Is... )
40
+ setindex! (A, v, I)
41
+ end
42
+
43
+ # we still dispatch to `Base.getindex(a, ::Int)` etc so that there's a single method to
44
+ # override when a back-end (e.g. with unified memory) wants to allow scalar indexing.
45
+
46
+ function Base. getindex (A:: AbstractGPUArray{T} , I:: Int ) where T
47
+ @boundscheck checkbounds (A, I)
9
48
assertscalar (" getindex" )
10
- i = Base. _to_linear_index (xs, I... )
11
49
x = Array {T} (undef, 1 )
12
- copyto! (x, 1 , xs, i , 1 )
50
+ copyto! (x, 1 , A, I , 1 )
13
51
return x[1 ]
14
52
end
15
53
16
- function Base. setindex! (xs:: AbstractGPUArray{T} , v:: T , I:: Integer... ) where T
54
+ function Base. setindex! (A:: AbstractGPUArray{T} , v, I:: Int ) where T
55
+ @boundscheck checkbounds (A, I)
17
56
assertscalar (" setindex!" )
18
- i = Base. _to_linear_index (xs, I... )
19
57
x = T[v]
20
- copyto! (xs, i , x, 1 , 1 )
21
- return xs
58
+ copyto! (A, I , x, 1 , 1 )
59
+ return A
22
60
end
23
61
24
- Base. setindex! (xs:: AbstractGPUArray , v, I:: Integer... ) =
25
- setindex! (xs, convert (eltype (xs), v), I... )
26
-
62
+ # # vectorized indexing
27
63
28
- # basic indexing with cartesian indices
29
-
30
- Base. @propagate_inbounds Base. getindex (A:: AbstractGPUArray , I:: Union{Integer, CartesianIndex} ...) =
31
- A[Base. to_indices (A, I)... ]
32
- Base. @propagate_inbounds Base. setindex! (A:: AbstractGPUArray , v, I:: Union{Integer, CartesianIndex} ...) =
33
- (A[Base. to_indices (A, I)... ] = v; A)
34
-
35
-
36
- # generalized multidimensional indexing
37
-
38
- Base. getindex (A:: AbstractGPUArray , I... ) = _getindex (A, to_indices (A, I)... )
39
-
40
- function _getindex (src:: AbstractGPUArray , Is... )
64
+ function vectorized_getindex (src:: AbstractGPUArray , Is... )
41
65
shape = Base. index_shape (Is... )
42
66
dest = similar (src, shape)
43
67
any (isempty, Is) && return dest # indexing with empty array
44
68
idims = map (length, Is)
45
69
46
- AT = typeof (src). name. wrapper
47
70
# NOTE: we are pretty liberal here supporting non-GPU indices...
48
- gpu_call (getindex_kernel, dest, src, idims, adapt (AT, Is)... )
71
+ Is = map (x-> adapt (ToGPU (src), x), Is)
72
+ @boundscheck checkbounds (src, Is... )
73
+
74
+ gpu_call (getindex_kernel, dest, src, idims, Is... )
49
75
return dest
50
76
end
51
77
61
87
end
62
88
end
63
89
64
- Base. setindex! (A:: AbstractGPUArray , v, I... ) = _setindex! (A, v, to_indices (A, I)... )
65
-
66
- function _setindex! (dest:: AbstractGPUArray , src, Is... )
90
+ function vectorized_setindex! (dest:: AbstractGPUArray , src, Is... )
67
91
isempty (Is) && return dest
68
92
idims = length .(Is)
69
93
len = prod (idims)
@@ -76,9 +100,11 @@ function _setindex!(dest::AbstractGPUArray, src, Is...)
76
100
end
77
101
end
78
102
79
- AT = typeof (dest). name. wrapper
80
- # NOTE: we are pretty liberal here supporting non-GPU sources and indices...
81
- gpu_call (setindex_kernel, dest, adapt (AT, src), idims, len, adapt (AT, Is)... ;
103
+ # NOTE: we are pretty liberal here supporting non-GPU indices...
104
+ Is = map (x-> adapt (ToGPU (dest), x), Is)
105
+ @boundscheck checkbounds (dest, Is... )
106
+
107
+ gpu_call (setindex_kernel, dest, adapt (ToGPU (dest), src), idims, len, Is... ;
82
108
elements= len)
83
109
return dest
84
110
end
96
122
end
97
123
98
124
99
- # # find*
125
+ # bounds checking
126
+
127
+ # indices residing on the GPU should be bounds-checked on the GPU to avoid iteration.
128
+
129
+ # not all wrapped GPU arrays make sense as indices, so we use a subset of `AnyGPUArray`
130
+ const IndexGPUArray{T} = Union{AbstractGPUArray{T},
131
+ SubArray{T, <: Any , <: AbstractGPUArray },
132
+ LinearAlgebra. Adjoint{T}}
133
+
134
+ @inline function Base. checkindex (:: Type{Bool} , inds:: AbstractUnitRange , I:: IndexGPUArray )
135
+ all (broadcast (I) do i
136
+ Base. checkindex (Bool, inds, i)
137
+ end )
138
+ end
139
+
140
+ @inline function Base. checkindex (:: Type{Bool} , inds:: Tuple ,
141
+ I:: IndexGPUArray{<:CartesianIndex} )
142
+ all (broadcast (I) do i
143
+ Base. checkbounds_indices (Bool, inds, (i,))
144
+ end )
145
+ end
146
+
147
+
148
+ # find*
100
149
101
150
# simple array type that returns the index used to access an element, while
102
151
# retaining the dimensionality of the original array. this can be used to
@@ -107,15 +156,15 @@ struct EachIndex{T,N,IS} <: AbstractArray{T,N}
107
156
dims:: NTuple{N,Int}
108
157
indices:: IS
109
158
end
110
- EachIndex (xs :: AbstractArray ) =
111
- EachIndex {typeof(firstindex(xs )), ndims(xs ), typeof(eachindex(xs ))} (
112
- size (xs ), eachindex (xs ))
159
+ EachIndex (A :: AbstractArray ) =
160
+ EachIndex {typeof(firstindex(A )), ndims(A ), typeof(eachindex(A ))} (
161
+ size (A ), eachindex (A ))
113
162
Base. size (ei:: EachIndex ) = ei. dims
114
163
Base. getindex (ei:: EachIndex , i:: Int ) = ei. indices[i]
115
164
Base. IndexStyle (:: Type{<:EachIndex} ) = Base. IndexLinear ()
116
165
117
- function Base. findfirst (f:: Function , xs :: AnyGPUArray )
118
- indices = EachIndex (xs )
166
+ function Base. findfirst (f:: Function , A :: AnyGPUArray )
167
+ indices = EachIndex (A )
119
168
dummy_index = first (indices)
120
169
121
170
# given two pairs of (istrue, index), return the one with the smallest index
@@ -130,23 +179,23 @@ function Base.findfirst(f::Function, xs::AnyGPUArray)
130
179
return (false , dummy_index)
131
180
end
132
181
133
- res = mapreduce ((x, y)-> (f (x), y), reduction, xs , indices;
182
+ res = mapreduce ((x, y)-> (f (x), y), reduction, A , indices;
134
183
init = (false , dummy_index))
135
184
if res[1 ]
136
185
# out of consistency with Base.findarray, return a CartesianIndex
137
186
# when the input is a multidimensional array
138
- ndims (xs ) == 1 && return res[2 ]
139
- return CartesianIndices (xs )[res[2 ]]
187
+ ndims (A ) == 1 && return res[2 ]
188
+ return CartesianIndices (A )[res[2 ]]
140
189
else
141
190
return nothing
142
191
end
143
192
end
144
193
145
- Base. findfirst (xs :: AnyGPUArray{Bool} ) = findfirst (identity, xs )
194
+ Base. findfirst (A :: AnyGPUArray{Bool} ) = findfirst (identity, A )
146
195
147
- function findminmax (binop, xs :: AnyGPUArray ; init, dims)
148
- indices = EachIndex (xs )
149
- dummy_index = firstindex (xs )
196
+ function findminmax (binop, A :: AnyGPUArray ; init, dims)
197
+ indices = EachIndex (A )
198
+ dummy_index = firstindex (A )
150
199
151
200
function reduction (t1, t2)
152
201
(x, i), (y, j) = t1, t2
@@ -157,16 +206,16 @@ function findminmax(binop, xs::AnyGPUArray; init, dims)
157
206
end
158
207
159
208
if dims == Colon ()
160
- res = mapreduce (tuple, reduction, xs , indices; init = (init, dummy_index))
209
+ res = mapreduce (tuple, reduction, A , indices; init = (init, dummy_index))
161
210
162
211
# out of consistency with Base.findarray, return a CartesianIndex
163
212
# when the input is a multidimensional array
164
- return (res[1 ], ndims (xs ) == 1 ? res[2 ] : CartesianIndices (xs )[res[2 ]])
213
+ return (res[1 ], ndims (A ) == 1 ? res[2 ] : CartesianIndices (A )[res[2 ]])
165
214
else
166
- res = mapreduce (tuple, reduction, xs , indices;
215
+ res = mapreduce (tuple, reduction, A , indices;
167
216
init = (init, dummy_index), dims= dims)
168
217
vals = map (x-> x[1 ], res)
169
- inds = map (x-> ndims (xs ) == 1 ? x[2 ] : CartesianIndices (xs )[x[2 ]], res)
218
+ inds = map (x-> ndims (A ) == 1 ? x[2 ] : CartesianIndices (A )[x[2 ]], res)
170
219
return (vals, inds)
171
220
end
172
221
end
0 commit comments