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, bool useFloat16, int kOverride = -1) {
26  int numVecs = faiss::gpu::randVal(1000, 20000);
27  int dim = faiss::gpu::randVal(50, 800);
28  int numQuery = faiss::gpu::randVal(1, 512);
29 
30  // Due to loss of precision in a float16 accumulator, for large k,
31  // the number of differences is pretty huge. Restrict ourselves to a
32  // fairly small `k` for float16
33  int k = useFloat16 ?
34  std::min(faiss::gpu::randVal(1, 50), numVecs) :
35  std::min(faiss::gpu::randVal(1, 1024), numVecs);
36  if (kOverride > 0) {
37  k = kOverride;
38  }
39 
40  faiss::IndexFlatIP cpuIndexIP(dim);
41  faiss::IndexFlatL2 cpuIndexL2(dim);
42 
43  faiss::IndexFlat* cpuIndex =
44  useL2 ? (faiss::IndexFlat*) &cpuIndexL2 : (faiss::IndexFlat*) &cpuIndexIP;
45 
46  // Construct on a random device to test multi-device, if we have
47  // multiple devices
48  int device = faiss::gpu::randVal(0, faiss::gpu::getNumDevices() - 1);
49 
51  res.noTempMemory();
52  faiss::gpu::GpuIndexFlatIP gpuIndexIP(&res, device, dim, useFloat16);
53  faiss::gpu::GpuIndexFlatL2 gpuIndexL2(&res, device, dim, useFloat16);
54 
55  faiss::gpu::GpuIndexFlat* gpuIndex =
56  useL2 ? (faiss::gpu::GpuIndexFlat*) &gpuIndexL2 :
57  (faiss::gpu::GpuIndexFlat*) &gpuIndexIP;
58 
59  std::vector<float> vecs = faiss::gpu::randVecs(numVecs, dim);
60  cpuIndex->add(numVecs, vecs.data());
61  gpuIndex->add(numVecs, vecs.data());
62 
63  std::stringstream str;
64  str << (useL2 ? "L2" : "IP") << " numVecs " << numVecs
65  << " dim " << dim
66  << " useFloat16 " << useFloat16
67  << " numQuery " << numQuery
68  << " k " << k;
69 
70  // To some extent, we depend upon the relative error for the test
71  // for float16
72  faiss::gpu::compareIndices(*cpuIndex, *gpuIndex, numQuery, dim, k, str.str(),
73  useFloat16 ? kF16MaxRelErr : kF32MaxRelErr,
74  // FIXME: the fp16 bounds are
75  // useless when math (the accumulator) is
76  // in fp16. Figure out another way to test
77  useFloat16 ? 0.99f : 0.1f,
78  useFloat16 ? 0.65f : 0.015f);
79 }
80 
81 TEST(TestGpuIndexFlat, IP_Float32) {
82  for (int tries = 0; tries < 10; ++tries) {
83  faiss::gpu::newTestSeed();
84  testFlat(false, false);
85  }
86 }
87 
88 TEST(TestGpuIndexFlat, L2_Float32) {
89  for (int tries = 0; tries < 10; ++tries) {
90  faiss::gpu::newTestSeed();
91  testFlat(true, false);
92  }
93 }
94 
95 // test specialized k == 1 codepath
96 TEST(TestGpuIndexFlat, L2_Float32_K1) {
97  for (int tries = 0; tries < 5; ++tries) {
98  faiss::gpu::newTestSeed();
99  testFlat(true, false, 1);
100  }
101 }
102 
103 TEST(TestGpuIndexFlat, IP_Float16) {
104  for (int tries = 0; tries < 10; ++tries) {
105  faiss::gpu::newTestSeed();
106  testFlat(false, true);
107  }
108 }
109 
110 TEST(TestGpuIndexFlat, L2_Float16) {
111  for (int tries = 0; tries < 10; ++tries) {
112  faiss::gpu::newTestSeed();
113  testFlat(true, true);
114  }
115 }
116 
117 // test specialized k == 1 codepath
118 TEST(TestGpuIndexFlat, L2_Float16_K1) {
119  for (int tries = 0; tries < 5; ++tries) {
120  faiss::gpu::newTestSeed();
121  testFlat(true, true, 1);
122  }
123 }
124 
125 TEST(TestGpuIndexFlat, QueryEmpty) {
127  res.noTempMemory();
128 
129  int dim = 128;
130  faiss::gpu::GpuIndexFlatL2 gpuIndex(&res, 0, dim, false);
131 
132  // Querying an empty index should not blow up, and just return
133  // (FLT_MAX, -1)
134  int numQuery = 10;
135  int k = 50;
136  std::vector<float> queries(numQuery * dim, 1.0f);
137 
138  std::vector<float> dist(numQuery * k, 0);
139  std::vector<faiss::Index::idx_t> ind(numQuery * k);
140 
141  gpuIndex.search(numQuery, queries.data(), k, dist.data(), ind.data());
142 
143  for (auto d : dist) {
144  EXPECT_EQ(d, std::numeric_limits<float>::max());
145  }
146 
147  for (auto i : ind) {
148  EXPECT_EQ(i, -1);
149  }
150 }
151 
152 TEST(TestGpuIndexFlat, CopyFrom) {
153  faiss::gpu::newTestSeed();
154 
155  int numVecs = faiss::gpu::randVal(100, 200);
156  int dim = faiss::gpu::randVal(1, 1000);
157 
158  faiss::IndexFlatL2 cpuIndex(dim);
159 
160  std::vector<float> vecs = faiss::gpu::randVecs(numVecs, dim);
161  cpuIndex.add(numVecs, vecs.data());
162 
164  res.noTempMemory();
165 
166  // Fill with garbage values
167  int device = faiss::gpu::randVal(0, faiss::gpu::getNumDevices() - 1);
168  faiss::gpu::GpuIndexFlatL2 gpuIndex(&res, device, 2000, false);
169  gpuIndex.copyFrom(&cpuIndex);
170 
171  EXPECT_EQ(cpuIndex.ntotal, gpuIndex.ntotal);
172  EXPECT_EQ(gpuIndex.ntotal, numVecs);
173 
174  EXPECT_EQ(cpuIndex.d, gpuIndex.d);
175  EXPECT_EQ(cpuIndex.d, dim);
176 
177  int idx = faiss::gpu::randVal(0, numVecs - 1);
178 
179  std::vector<float> gpuVals(dim);
180  gpuIndex.reconstruct(idx, gpuVals.data());
181 
182  std::vector<float> cpuVals(dim);
183  cpuIndex.reconstruct(idx, cpuVals.data());
184 
185  EXPECT_EQ(gpuVals, cpuVals);
186 }
187 
188 TEST(TestGpuIndexFlat, CopyTo) {
189  faiss::gpu::newTestSeed();
190 
192  res.noTempMemory();
193 
194  int numVecs = faiss::gpu::randVal(100, 200);
195  int dim = faiss::gpu::randVal(1, 1000);
196 
197  int device = faiss::gpu::randVal(0, faiss::gpu::getNumDevices() - 1);
198  faiss::gpu::GpuIndexFlatL2 gpuIndex(&res, device, dim, false);
199 
200  std::vector<float> vecs = faiss::gpu::randVecs(numVecs, dim);
201  gpuIndex.add(numVecs, vecs.data());
202 
203  // Fill with garbage values
204  faiss::IndexFlatL2 cpuIndex(2000);
205  gpuIndex.copyTo(&cpuIndex);
206 
207  EXPECT_EQ(cpuIndex.ntotal, gpuIndex.ntotal);
208  EXPECT_EQ(gpuIndex.ntotal, numVecs);
209 
210  EXPECT_EQ(cpuIndex.d, gpuIndex.d);
211  EXPECT_EQ(cpuIndex.d, dim);
212 
213  int idx = faiss::gpu::randVal(0, numVecs - 1);
214 
215  std::vector<float> gpuVals(dim);
216  gpuIndex.reconstruct(idx, gpuVals.data());
217 
218  std::vector<float> cpuVals(dim);
219  cpuIndex.reconstruct(idx, cpuVals.data());
220 
221  EXPECT_EQ(gpuVals, cpuVals);
222 }
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:81
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