Skip to content

Commit 9ff48f8

Browse files
mdouzefacebook-github-bot
authored andcommitted
Add benchmark to measure the ResultHandler overhead
Summary: There seems to be a performance regression in Faiss after the ResultHandler's introduction. This test attemtps to reproduce it. Differential Revision: D91784507
1 parent 8cc9a31 commit 9ff48f8

File tree

2 files changed

+181
-0
lines changed

2 files changed

+181
-0
lines changed

benchs/CMakeLists.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,7 @@
88
add_executable(bench_ivf_selector EXCLUDE_FROM_ALL bench_ivf_selector.cpp)
99
target_link_libraries(bench_ivf_selector PRIVATE faiss)
1010

11+
add_executable(bench_result_handler_overhead EXCLUDE_FROM_ALL
12+
bench_result_handler_overhead.cpp)
13+
target_link_libraries(bench_result_handler_overhead
14+
PRIVATE faiss OpenMP::OpenMP_CXX)
Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
/*
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
#include <faiss/AutoTune.h>
9+
#include <faiss/Index.h>
10+
#include <faiss/index_factory.h>
11+
#include <faiss/utils/random.h>
12+
#include <faiss/utils/utils.h>
13+
#include <omp.h>
14+
15+
#include <iomanip>
16+
#include <iostream>
17+
#include <map>
18+
#include <memory>
19+
#include <vector>
20+
21+
namespace faiss {
22+
23+
namespace {
24+
25+
constexpr int nb = 100000;
26+
constexpr int nq = 1000;
27+
constexpr int nrun = 5;
28+
29+
struct IndexData {
30+
std::unique_ptr<Index> index;
31+
std::vector<float> xq;
32+
};
33+
34+
struct BenchmarkResult {
35+
std::string index_factory;
36+
int d;
37+
int k;
38+
int nprobe;
39+
double time_per_query_us;
40+
};
41+
42+
double run_search(
43+
IndexData& data,
44+
int d,
45+
int k,
46+
int nprobe,
47+
const char* factory_string) {
48+
ParameterSpace().set_index_parameter(data.index.get(), "nprobe", nprobe);
49+
50+
omp_set_num_threads(1);
51+
52+
std::vector<float> distances(nq * k);
53+
std::vector<idx_t> labels(nq * k);
54+
55+
// Warmup
56+
data.index->search(nq, data.xq.data(), k, distances.data(), labels.data());
57+
58+
// Timed runs - stop if total time exceeds 2 seconds
59+
double t0 = getmillisecs();
60+
int effective_runs = 0;
61+
for (int run = 0; run < nrun; run++) {
62+
data.index->search(
63+
nq, data.xq.data(), k, distances.data(), labels.data());
64+
effective_runs++;
65+
if (getmillisecs() - t0 > 2000.0) {
66+
break;
67+
}
68+
}
69+
double t1 = getmillisecs();
70+
71+
double time_per_query_us = (t1 - t0) / effective_runs / nq * 1000.0;
72+
return time_per_query_us;
73+
}
74+
75+
IndexData build_index(int d, const char* factory_string) {
76+
omp_set_num_threads(32);
77+
78+
int nt = std::max(nb, 1024);
79+
80+
std::vector<float> xt(nt * d);
81+
std::vector<float> xb(nb * d);
82+
83+
rand_smooth_vectors(nt, d, xt.data(), 12345);
84+
rand_smooth_vectors(nb, d, xb.data(), 23456);
85+
86+
IndexData data;
87+
data.index.reset(index_factory(d, factory_string));
88+
data.index->train(nt, xt.data());
89+
data.index->add(nb, xb.data());
90+
91+
data.xq.resize(nq * d);
92+
rand_smooth_vectors(nq, d, data.xq.data(), 34567);
93+
94+
return data;
95+
}
96+
97+
void print_results_table(
98+
const std::string& index_factory,
99+
int d,
100+
const std::vector<BenchmarkResult>& results) {
101+
std::vector<int> ks_list = {1, 4, 16, 64};
102+
std::vector<int> nprobes_list = {1, 4, 16, 64};
103+
104+
std::map<std::pair<int, int>, double> result_map;
105+
for (const auto& r : results) {
106+
result_map[{r.k, r.nprobe}] = r.time_per_query_us;
107+
}
108+
109+
std::cout << "\n" << index_factory << " d=" << d << " (time in us/query)\n";
110+
std::cout << std::string(60, '-') << "\n";
111+
112+
std::cout << std::setw(8) << "k \\ np"
113+
<< " |";
114+
for (int np : nprobes_list) {
115+
std::cout << std::setw(10) << np << " |";
116+
}
117+
std::cout << "\n";
118+
std::cout << std::string(60, '-') << "\n";
119+
120+
for (int k : ks_list) {
121+
std::cout << std::setw(8) << k << " |";
122+
for (int np : nprobes_list) {
123+
auto it = result_map.find({k, np});
124+
if (it != result_map.end()) {
125+
std::cout << std::setw(10) << std::fixed << std::setprecision(1)
126+
<< it->second << " |";
127+
} else {
128+
std::cout << std::setw(10) << "N/A"
129+
<< " |";
130+
}
131+
}
132+
std::cout << "\n";
133+
}
134+
}
135+
136+
} // namespace
137+
138+
} // namespace faiss
139+
140+
int main() {
141+
/*
142+
std::vector<std::string> indexes = {
143+
"IVF256,SQ4", "IVF256,RaBitQ", "IVF256,SQfp16"};
144+
std::vector<int> dims = {32, 64, 128};
145+
std::vector<int> ks = {1, 4, 16, 64};
146+
std::vector<int> nprobes = {1, 4, 16, 64};
147+
*/
148+
std::vector<std::string> indexes = {
149+
"IVF256,SQ4",
150+
};
151+
std::vector<int> dims = {32};
152+
std::vector<int> ks = {1, 4, 16};
153+
std::vector<int> nprobes = {1, 4, 16};
154+
155+
for (const auto& index_factory : indexes) {
156+
for (int d : dims) {
157+
std::cout << "Building " << index_factory << " d=" << d << "..."
158+
<< std::flush;
159+
faiss::IndexData data =
160+
faiss::build_index(d, index_factory.c_str());
161+
std::cout << " done\n";
162+
163+
std::vector<faiss::BenchmarkResult> results;
164+
for (int k : ks) {
165+
for (int nprobe : nprobes) {
166+
double time_us = faiss::run_search(
167+
data, d, k, nprobe, index_factory.c_str());
168+
results.push_back({index_factory, d, k, nprobe, time_us});
169+
}
170+
}
171+
172+
faiss::print_results_table(index_factory, d, results);
173+
}
174+
}
175+
176+
return 0;
177+
}

0 commit comments

Comments
 (0)