-
Notifications
You must be signed in to change notification settings - Fork 4.4k
Expand file tree
/
Copy pathgpu_wrappers.py
More file actions
293 lines (248 loc) · 8.98 KB
/
gpu_wrappers.py
File metadata and controls
293 lines (248 loc) · 8.98 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
# Copyright (c) Facebook, Inc. and its affiliates.
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
# @nolint
# not linting this file because it imports * from swigfaiss, which
# causes a ton of useless warnings.
import numpy as np
from faiss.loader import *
###########################################
# GPU functions
###########################################
def index_cpu_to_gpu_multiple_py(resources, index, co=None, gpus=None):
""" builds the C++ vectors for the GPU indices and the
resources. Handles the case where the resources are assigned to
the list of GPUs """
if gpus is None:
gpus = range(len(resources))
vres = GpuResourcesVector()
vdev = Int32Vector()
for i, res in zip(gpus, resources):
vdev.push_back(i)
vres.push_back(res)
if isinstance(index, IndexBinary):
return index_binary_cpu_to_gpu_multiple(vres, vdev, index, co)
else:
return index_cpu_to_gpu_multiple(vres, vdev, index, co)
def index_cpu_to_all_gpus(index, co=None, ngpu=-1):
index_gpu = index_cpu_to_gpus_list(index, co=co, gpus=None, ngpu=ngpu)
return index_gpu
def index_cpu_to_gpus_list(index, co=None, gpus=None, ngpu=-1):
""" Here we can pass list of GPU ids as a parameter or ngpu to
use first n GPU's. gpus mut be a list or None.
co is a GpuMultipleClonerOptions
"""
if (gpus is None) and (ngpu == -1): # All blank
gpus = range(get_num_gpus())
elif (gpus is None) and (ngpu != -1): # Get number of GPU's only
gpus = range(ngpu)
res = [StandardGpuResources() for _ in gpus]
index_gpu = index_cpu_to_gpu_multiple_py(res, index, co, gpus)
return index_gpu
# allows numpy ndarray usage with bfKnn
def knn_gpu(res, xq, xb, k, D=None, I=None, metric=METRIC_L2, device=-1, use_cuvs=False, vectorsMemoryLimit=0, queriesMemoryLimit=0):
"""
Compute the k nearest neighbors of a vector on one GPU without constructing an index
Parameters
----------
res : StandardGpuResources
GPU resources to use during computation
xq : array_like
Query vectors, shape (nq, d) where d is appropriate for the index.
`dtype` must be float32.
xb : array_like
Database vectors, shape (nb, d) where d is appropriate for the index.
`dtype` must be float32.
k : int
Number of nearest neighbors.
D : array_like, optional
Output array for distances of the nearest neighbors, shape (nq, k)
I : array_like, optional
Output array for the nearest neighbors, shape (nq, k)
metric : MetricType, optional
Distance measure to use (either METRIC_L2 or METRIC_INNER_PRODUCT)
device: int, optional
Which CUDA device in the system to run the search on. -1 indicates that
the current thread-local device state (via cudaGetDevice) should be used
(can also be set via torch.cuda.set_device in PyTorch)
Otherwise, an integer 0 <= device < numDevices indicates the GPU on which
the computation should be run
vectorsMemoryLimit: int, optional
queriesMemoryLimit: int, optional
Memory limits for vectors and queries.
If not 0, the GPU will use at most this amount of memory
for vectors and queries respectively.
Vectors are broken up into chunks of size vectorsMemoryLimit,
and queries are broken up into chunks of size queriesMemoryLimit,
including the memory required for the results.
Returns
-------
D : array_like
Distances of the nearest neighbors, shape (nq, k)
I : array_like
Labels of the nearest neighbors, shape (nq, k)
"""
nq, d = xq.shape
if xq.flags.c_contiguous:
xq_row_major = True
elif xq.flags.f_contiguous:
xq = xq.T
xq_row_major = False
else:
xq = np.ascontiguousarray(xq, dtype='float32')
xq_row_major = True
xq_ptr = swig_ptr(xq)
if xq.dtype == np.float32:
xq_type = DistanceDataType_F32
elif xq.dtype == np.float16:
xq_type = DistanceDataType_F16
else:
raise TypeError('xq must be f32 or f16')
nb, d2 = xb.shape
assert d2 == d
if xb.flags.c_contiguous:
xb_row_major = True
elif xb.flags.f_contiguous:
xb = xb.T
xb_row_major = False
else:
xb = np.ascontiguousarray(xb, dtype='float32')
xb_row_major = True
xb_ptr = swig_ptr(xb)
if xb.dtype == np.float32:
xb_type = DistanceDataType_F32
elif xb.dtype == np.float16:
xb_type = DistanceDataType_F16
else:
raise TypeError('xb must be float32 or float16')
if D is None:
D = np.empty((nq, k), dtype=np.float32)
else:
assert D.shape == (nq, k)
# interface takes void*, we need to check this
assert D.dtype == np.float32
D_ptr = swig_ptr(D)
if I is None:
I = np.empty((nq, k), dtype=np.int64)
else:
assert I.shape == (nq, k)
I_ptr = swig_ptr(I)
if I.dtype == np.int64:
I_type = IndicesDataType_I64
elif I.dtype == I.dtype == np.int32:
I_type = IndicesDataType_I32
else:
raise TypeError('I must be i64 or i32')
args = GpuDistanceParams()
args.metric = metric
args.k = k
args.dims = d
args.vectors = xb_ptr
args.vectorsRowMajor = xb_row_major
args.vectorType = xb_type
args.numVectors = nb
args.queries = xq_ptr
args.queriesRowMajor = xq_row_major
args.queryType = xq_type
args.numQueries = nq
args.outDistances = D_ptr
args.outIndices = I_ptr
args.outIndicesType = I_type
args.device = device
args.use_cuvs = use_cuvs
# no stream synchronization needed, inputs and outputs are guaranteed to
# be on the CPU (numpy arrays)
if vectorsMemoryLimit > 0 or queriesMemoryLimit > 0:
bfKnn_tiling(res, args, vectorsMemoryLimit, queriesMemoryLimit)
else:
bfKnn(res, args)
return D, I
# allows numpy ndarray usage with bfKnn for all pairwise distances
def pairwise_distance_gpu(res, xq, xb, D=None, metric=METRIC_L2, device=-1):
"""
Compute all pairwise distances between xq and xb on one GPU without constructing an index
Parameters
----------
res : StandardGpuResources
GPU resources to use during computation
xq : array_like
Query vectors, shape (nq, d) where d is appropriate for the index.
`dtype` must be float32.
xb : array_like
Database vectors, shape (nb, d) where d is appropriate for the index.
`dtype` must be float32.
D : array_like, optional
Output array for all pairwise distances, shape (nq, nb)
metric : MetricType, optional
Distance measure to use (either METRIC_L2 or METRIC_INNER_PRODUCT)
device: int, optional
Which CUDA device in the system to run the search on. -1 indicates that
the current thread-local device state (via cudaGetDevice) should be used
(can also be set via torch.cuda.set_device in PyTorch)
Otherwise, an integer 0 <= device < numDevices indicates the GPU on which
the computation should be run
Returns
-------
D : array_like
All pairwise distances, shape (nq, nb)
"""
nq, d = xq.shape
if xq.flags.c_contiguous:
xq_row_major = True
elif xq.flags.f_contiguous:
xq = xq.T
xq_row_major = False
else:
raise TypeError(
'xq matrix should be row (C) or column-major (Fortran)')
xq_ptr = swig_ptr(xq)
if xq.dtype == np.float32:
xq_type = DistanceDataType_F32
elif xq.dtype == np.float16:
xq_type = DistanceDataType_F16
else:
xq = np.ascontiguousarray(xb, dtype='float32')
xq_row_major = True
nb, d2 = xb.shape
assert d2 == d
if xb.flags.c_contiguous:
xb_row_major = True
elif xb.flags.f_contiguous:
xb = xb.T
xb_row_major = False
else:
xb = np.ascontiguousarray(xb, dtype='float32')
xb_row_major = True
xb_ptr = swig_ptr(xb)
if xb.dtype == np.float32:
xb_type = DistanceDataType_F32
elif xb.dtype == np.float16:
xb_type = DistanceDataType_F16
else:
raise TypeError('xb must be float32 or float16')
if D is None:
D = np.empty((nq, nb), dtype=np.float32)
else:
assert D.shape == (nq, nb)
# interface takes void*, we need to check this
assert D.dtype == np.float32
D_ptr = swig_ptr(D)
args = GpuDistanceParams()
args.metric = metric
args.k = -1 # selects all pairwise distances
args.dims = d
args.vectors = xb_ptr
args.vectorsRowMajor = xb_row_major
args.vectorType = xb_type
args.numVectors = nb
args.queries = xq_ptr
args.queriesRowMajor = xq_row_major
args.queryType = xq_type
args.numQueries = nq
args.outDistances = D_ptr
args.device = device
# no stream synchronization needed, inputs and outputs are guaranteed to
# be on the CPU (numpy arrays)
bfKnn(res, args)
return D