Faiss
 All Classes Namespaces Functions Variables Typedefs Enumerations Enumerator Friends
TestGpuIndexFlat.cpp
1 
2 /**
3  * Copyright (c) 2015-present, Facebook, Inc.
4  * All rights reserved.
5  *
6  * This source code is licensed under the CC-by-NC license found in the
7  * LICENSE file in the root directory of this source tree.
8  */
9 
10 // Copyright 2004-present Facebook. All Rights Reserved.
11 
12 #include "../../IndexFlat.h"
13 #include "../GpuIndexFlat.h"
14 #include "../StandardGpuResources.h"
15 #include "../utils/DeviceUtils.h"
16 #include "../test/TestUtils.h"
17 #include <gtest/gtest.h>
18 #include <sstream>
19 #include <vector>
20 
21 // FIXME: figure out a better way to test fp16
22 constexpr float kF16MaxRelErr = 0.07f;
23 constexpr float kF32MaxRelErr = 6e-3f;
24 
25 void testFlat(bool useL2,
26  bool useFloat16,
27  bool useTransposed,
28  int kOverride = -1) {
29  int numVecs = faiss::gpu::randVal(1000, 20000);
30  int dim = faiss::gpu::randVal(50, 800);
31  int numQuery = faiss::gpu::randVal(1, 512);
32 
33  // Due to loss of precision in a float16 accumulator, for large k,
34  // the number of differences is pretty huge. Restrict ourselves to a
35  // fairly small `k` for float16
36  int k = useFloat16 ?
37  std::min(faiss::gpu::randVal(1, 50), numVecs) :
38  std::min(faiss::gpu::randVal(1, 1024), numVecs);
39  if (kOverride > 0) {
40  k = kOverride;
41  }
42 
43  faiss::IndexFlatIP cpuIndexIP(dim);
44  faiss::IndexFlatL2 cpuIndexL2(dim);
45 
46  faiss::IndexFlat* cpuIndex =
47  useL2 ? (faiss::IndexFlat*) &cpuIndexL2 : (faiss::IndexFlat*) &cpuIndexIP;
48 
49  // Construct on a random device to test multi-device, if we have
50  // multiple devices
51  int device = faiss::gpu::randVal(0, faiss::gpu::getNumDevices() - 1);
52 
54  res.noTempMemory();
55 
56 
58  config.device = device;
59  config.useFloat16 = useFloat16;
60  config.storeTransposed = useTransposed;
61 
62  faiss::gpu::GpuIndexFlatIP gpuIndexIP(&res, dim, config);
63  faiss::gpu::GpuIndexFlatL2 gpuIndexL2(&res, dim, config);
64 
65  faiss::gpu::GpuIndexFlat* gpuIndex =
66  useL2 ? (faiss::gpu::GpuIndexFlat*) &gpuIndexL2 :
67  (faiss::gpu::GpuIndexFlat*) &gpuIndexIP;
68 
69  std::vector<float> vecs = faiss::gpu::randVecs(numVecs, dim);
70  cpuIndex->add(numVecs, vecs.data());
71  gpuIndex->add(numVecs, vecs.data());
72 
73  std::stringstream str;
74  str << (useL2 ? "L2" : "IP") << " numVecs " << numVecs
75  << " dim " << dim
76  << " useFloat16 " << useFloat16
77  << " transposed " << useTransposed
78  << " numQuery " << numQuery
79  << " k " << k;
80 
81  // To some extent, we depend upon the relative error for the test
82  // for float16
83  faiss::gpu::compareIndices(*cpuIndex, *gpuIndex, numQuery, dim, k, str.str(),
84  useFloat16 ? kF16MaxRelErr : kF32MaxRelErr,
85  // FIXME: the fp16 bounds are
86  // useless when math (the accumulator) is
87  // in fp16. Figure out another way to test
88  useFloat16 ? 0.99f : 0.1f,
89  useFloat16 ? 0.65f : 0.015f);
90 }
91 
92 TEST(TestGpuIndexFlat, IP_Float32) {
93  for (int tries = 0; tries < 5; ++tries) {
94  faiss::gpu::newTestSeed();
95  testFlat(false, false, false);
96  testFlat(false, false, true);
97  }
98 }
99 
100 TEST(TestGpuIndexFlat, L2_Float32) {
101  for (int tries = 0; tries < 5; ++tries) {
102  faiss::gpu::newTestSeed();
103  testFlat(true, false, false);
104  testFlat(true, false, true);
105  }
106 }
107 
108 // test specialized k == 1 codepath
109 TEST(TestGpuIndexFlat, L2_Float32_K1) {
110  for (int tries = 0; tries < 5; ++tries) {
111  faiss::gpu::newTestSeed();
112  testFlat(true, false, false, 1);
113  testFlat(true, false, true, 1);
114  }
115 }
116 
117 TEST(TestGpuIndexFlat, IP_Float16) {
118  for (int tries = 0; tries < 5; ++tries) {
119  faiss::gpu::newTestSeed();
120  testFlat(false, true, false);
121  testFlat(false, true, false);
122  }
123 }
124 
125 TEST(TestGpuIndexFlat, L2_Float16) {
126  for (int tries = 0; tries < 5; ++tries) {
127  faiss::gpu::newTestSeed();
128  testFlat(true, true, false);
129  testFlat(true, true, true);
130  }
131 }
132 
133 // test specialized k == 1 codepath
134 TEST(TestGpuIndexFlat, L2_Float16_K1) {
135  for (int tries = 0; tries < 5; ++tries) {
136  faiss::gpu::newTestSeed();
137  testFlat(true, true, false, 1);
138  testFlat(true, true, true, 1);
139  }
140 }
141 
142 TEST(TestGpuIndexFlat, QueryEmpty) {
144  res.noTempMemory();
145 
147  config.device = 0;
148  config.useFloat16 = false;
149  config.storeTransposed = false;
150 
151  int dim = 128;
152  faiss::gpu::GpuIndexFlatL2 gpuIndex(&res, dim, config);
153 
154  // Querying an empty index should not blow up, and just return
155  // (FLT_MAX, -1)
156  int numQuery = 10;
157  int k = 50;
158  std::vector<float> queries(numQuery * dim, 1.0f);
159 
160  std::vector<float> dist(numQuery * k, 0);
161  std::vector<faiss::Index::idx_t> ind(numQuery * k);
162 
163  gpuIndex.search(numQuery, queries.data(), k, dist.data(), ind.data());
164 
165  for (auto d : dist) {
166  EXPECT_EQ(d, std::numeric_limits<float>::max());
167  }
168 
169  for (auto i : ind) {
170  EXPECT_EQ(i, -1);
171  }
172 }
173 
174 TEST(TestGpuIndexFlat, CopyFrom) {
175  faiss::gpu::newTestSeed();
176 
177  int numVecs = faiss::gpu::randVal(100, 200);
178  int dim = faiss::gpu::randVal(1, 1000);
179 
180  faiss::IndexFlatL2 cpuIndex(dim);
181 
182  std::vector<float> vecs = faiss::gpu::randVecs(numVecs, dim);
183  cpuIndex.add(numVecs, vecs.data());
184 
186  res.noTempMemory();
187 
188  // Fill with garbage values
189  int device = faiss::gpu::randVal(0, faiss::gpu::getNumDevices() - 1);
190 
192  config.device = 0;
193  config.useFloat16 = false;
194  config.storeTransposed = false;
195 
196  faiss::gpu::GpuIndexFlatL2 gpuIndex(&res, 2000, config);
197  gpuIndex.copyFrom(&cpuIndex);
198 
199  EXPECT_EQ(cpuIndex.ntotal, gpuIndex.ntotal);
200  EXPECT_EQ(gpuIndex.ntotal, numVecs);
201 
202  EXPECT_EQ(cpuIndex.d, gpuIndex.d);
203  EXPECT_EQ(cpuIndex.d, dim);
204 
205  int idx = faiss::gpu::randVal(0, numVecs - 1);
206 
207  std::vector<float> gpuVals(dim);
208  gpuIndex.reconstruct(idx, gpuVals.data());
209 
210  std::vector<float> cpuVals(dim);
211  cpuIndex.reconstruct(idx, cpuVals.data());
212 
213  EXPECT_EQ(gpuVals, cpuVals);
214 }
215 
216 TEST(TestGpuIndexFlat, CopyTo) {
217  faiss::gpu::newTestSeed();
218 
220  res.noTempMemory();
221 
222  int numVecs = faiss::gpu::randVal(100, 200);
223  int dim = faiss::gpu::randVal(1, 1000);
224 
225  int device = faiss::gpu::randVal(0, faiss::gpu::getNumDevices() - 1);
226 
228  config.device = device;
229  config.useFloat16 = false;
230  config.storeTransposed = false;
231 
232  faiss::gpu::GpuIndexFlatL2 gpuIndex(&res, dim, config);
233 
234  std::vector<float> vecs = faiss::gpu::randVecs(numVecs, dim);
235  gpuIndex.add(numVecs, vecs.data());
236 
237  // Fill with garbage values
238  faiss::IndexFlatL2 cpuIndex(2000);
239  gpuIndex.copyTo(&cpuIndex);
240 
241  EXPECT_EQ(cpuIndex.ntotal, gpuIndex.ntotal);
242  EXPECT_EQ(gpuIndex.ntotal, numVecs);
243 
244  EXPECT_EQ(cpuIndex.d, gpuIndex.d);
245  EXPECT_EQ(cpuIndex.d, dim);
246 
247  int idx = faiss::gpu::randVal(0, numVecs - 1);
248 
249  std::vector<float> gpuVals(dim);
250  gpuIndex.reconstruct(idx, gpuVals.data());
251 
252  std::vector<float> cpuVals(dim);
253  cpuIndex.reconstruct(idx, cpuVals.data());
254 
255  EXPECT_EQ(gpuVals, cpuVals);
256 }
void copyTo(faiss::IndexFlat *index) const
virtual void reconstruct(idx_t key, float *recons) const override
Definition: IndexFlat.cpp:109
int d
vector dimension
Definition: Index.h:66
void reconstruct(faiss::Index::idx_t key, float *out) const override
idx_t ntotal
total nb of indexed vectors
Definition: Index.h:67
virtual void add(idx_t n, const float *x) override
Definition: IndexFlat.cpp:41
void copyFrom(const faiss::IndexFlat *index)
Definition: GpuIndexFlat.cu:80
void add(Index::idx_t n, const float *x) override
void search(faiss::Index::idx_t n, const float *x, faiss::Index::idx_t k, float *distances, faiss::Index::idx_t *labels) const override