Replace dispatch_HammingComputer with with_HammingComputer (#5126)

Summary:
Pull Request resolved: https://github.com/facebookresearch/faiss/pull/5126

Replace the dispatch_HammingComputer + Run_XXX consumer struct pattern with
with_HammingComputer that takes a C++20 template lambda directly. This
eliminates boilerplate wrapper structs across 8 files.

Before:
  struct Run_foo { using T = void; template<class HC, class... T> void f(T... a) { foo<HC>(a...); } };
  Run_foo r; dispatch_HammingComputer(code_size, r, args...);

After:
  with_HammingComputer(code_size, [&]<class HC>() { foo<HC>(args...); });

Reviewed By: algoriddle

Differential Revision: D101350351

fbshipit-source-id: 02a346e8c33ffdb49153cbe13415b748f0a1e847
This commit is contained in:
Matthijs Douze
2026-04-20 06:32:07 -07:00
committed by meta-codesync[bot]
parent ed7e1f2435
commit e23c66175d
8 changed files with 78 additions and 178 deletions
+4 -10
View File
@@ -309,14 +309,6 @@ struct FlatHammingDis : DistanceComputer {
}
};
struct BuildDistanceComputer {
using T = DistanceComputer*;
template <class HammingComputer>
DistanceComputer* f(IndexBinaryFlat* flat_storage) {
return new FlatHammingDis<HammingComputer>(*flat_storage);
}
};
} // namespace
DistanceComputer* IndexBinaryHNSW::get_distance_computer() const {
@@ -324,8 +316,10 @@ DistanceComputer* IndexBinaryHNSW::get_distance_computer() const {
FAISS_THROW_IF_NOT_MSG(
flat_storage != nullptr,
"IndexBinaryHNSW requires IndexBinaryFlat storage");
BuildDistanceComputer bd;
return dispatch_HammingComputer(code_size, bd, flat_storage);
return with_HammingComputer(
code_size, [&]<class HammingComputer>() -> DistanceComputer* {
return new FlatHammingDis<HammingComputer>(*flat_storage);
});
}
/**************************************************************
+7 -22
View File
@@ -175,14 +175,6 @@ void search_single_query_template(
} while (fe.next());
}
struct Run_search_single_query {
using T = void;
template <class HammingComputer, class... Types>
T f(Types*... args) {
search_single_query_template<HammingComputer>(*args...);
}
};
template <class SearchResults>
void search_single_query(
const IndexBinaryHash& index,
@@ -191,9 +183,10 @@ void search_single_query(
size_t& n0,
size_t& nlist,
size_t& ndis) {
Run_search_single_query r;
dispatch_HammingComputer(
index.code_size, r, &index, &q, &res, &n0, &nlist, &ndis);
with_HammingComputer(index.code_size, [&]<class HammingComputer>() {
search_single_query_template<HammingComputer>(
index, q, res, n0, nlist, ndis);
});
}
} // anonymous namespace
@@ -343,14 +336,6 @@ static void verify_shortlist(
}
}
struct Run_verify_shortlist {
using T = void;
template <class HammingComputer, class... Types>
void f(Types... args) {
verify_shortlist<HammingComputer>(args...);
}
};
template <class SearchResults>
void search_1_query_multihash(
const IndexBinaryMultiHash& index,
@@ -387,9 +372,9 @@ void search_1_query_multihash(
ndis += shortlist.size();
// verify shortlist
Run_verify_shortlist r;
dispatch_HammingComputer(
index.code_size, r, index.storage, xi, shortlist, res);
with_HammingComputer(index.code_size, [&]<class HammingComputer>() {
verify_shortlist<HammingComputer>(index.storage, xi, shortlist, res);
});
}
} // anonymous namespace
+18 -42
View File
@@ -784,40 +784,16 @@ void search_knn_hamming_per_invlist(
}
}
struct Run_search_knn_hamming_per_invlist {
using T = void;
template <class HammingComputer, class... Types>
void f(Types... args) {
search_knn_hamming_per_invlist<HammingComputer>(args...);
}
};
template <bool store_pairs>
struct Run_search_knn_hamming_count {
using T = void;
template <class HammingComputer, class... Types>
void f(Types... args) {
search_knn_hamming_count<HammingComputer, store_pairs>(args...);
}
};
struct BuildScanner {
using T = BinaryInvertedListScanner*;
template <class HammingComputer>
T f(size_t code_size, bool store_pairs) {
return new IVFBinaryScannerL2<HammingComputer>(code_size, store_pairs);
}
};
} // anonymous namespace
BinaryInvertedListScanner* IndexBinaryIVF::get_InvertedListScanner(
bool store_pairs) const {
BuildScanner bs;
return dispatch_HammingComputer(code_size, bs, code_size, store_pairs);
return with_HammingComputer(
code_size,
[&]<class HammingComputer>() -> BinaryInvertedListScanner* {
return new IVFBinaryScannerL2<HammingComputer>(
code_size, store_pairs);
});
}
void IndexBinaryIVF::search_preassigned(
@@ -831,23 +807,23 @@ void IndexBinaryIVF::search_preassigned(
bool store_pairs,
const IVFSearchParameters* params) const {
if (per_invlist_search) {
Run_search_knn_hamming_per_invlist r;
// clang-format off
dispatch_HammingComputer(
code_size, r, this, n, x, k,
cidx, cdis, dis, idx, store_pairs, params);
// clang-format on
with_HammingComputer(code_size, [&]<class HammingComputer>() {
search_knn_hamming_per_invlist<HammingComputer>(
this, n, x, k, cidx, cdis, dis, idx, store_pairs, params);
});
} else if (use_heap) {
search_knn_hamming_heap(
this, n, x, k, cidx, cdis, dis, idx, store_pairs, params);
} else if (store_pairs) { // !use_heap && store_pairs
Run_search_knn_hamming_count<true> r;
dispatch_HammingComputer(
code_size, r, this, n, x, cidx, k, dis, idx, params);
with_HammingComputer(code_size, [&]<class HammingComputer>() {
search_knn_hamming_count<HammingComputer, true>(
this, n, x, cidx, k, dis, idx, params);
});
} else { // !use_heap && !store_pairs
Run_search_knn_hamming_count<false> r;
dispatch_HammingComputer(
code_size, r, this, n, x, cidx, k, dis, idx, params);
with_HammingComputer(code_size, [&]<class HammingComputer>() {
search_knn_hamming_count<HammingComputer, false>(
this, n, x, cidx, k, dis, idx, params);
});
}
}
+4 -12
View File
@@ -1167,23 +1167,15 @@ struct IVFPQScannerT : QueryTables {
}
}
template <class SearchResultType>
struct Run_scan_list_polysemous_hc {
using T = void;
template <class HammingComputer, class... Types>
void f(const IVFPQScannerT* scanner, Types... args) {
scanner->scan_list_polysemous_hc<HammingComputer, SearchResultType>(
args...);
}
};
template <class SearchResultType>
void scan_list_polysemous(
size_t ncode,
const uint8_t* codes,
SearchResultType& res) const {
Run_scan_list_polysemous_hc<SearchResultType> r;
dispatch_HammingComputer(pq.code_size, r, this, ncode, codes, res);
with_HammingComputer(pq.code_size, [&]<class HammingComputer>() {
this->scan_list_polysemous_hc<HammingComputer, SearchResultType>(
ncode, codes, res);
});
}
};
+4 -11
View File
@@ -299,15 +299,6 @@ struct IVFScanner : InvertedListScanner {
}
};
struct BuildScanner {
using T = InvertedListScanner*;
template <class HammingComputer>
static T f(const IndexIVFSpectralHash* index, bool store_pairs) {
return new IVFScanner<HammingComputer>(index, store_pairs);
}
};
} // anonymous namespace
InvertedListScanner* IndexIVFSpectralHash::get_InvertedListScanner(
@@ -315,8 +306,10 @@ InvertedListScanner* IndexIVFSpectralHash::get_InvertedListScanner(
const IDSelector* sel,
const IVFSearchParameters*) const {
FAISS_THROW_IF_NOT(!sel);
BuildScanner bs;
return dispatch_HammingComputer(code_size, bs, this, store_pairs);
return with_HammingComputer(
code_size, [&]<class HammingComputer>() -> InvertedListScanner* {
return new IVFScanner<HammingComputer>(this, store_pairs);
});
}
void IndexIVFSpectralHash::replace_vt(VectorTransform* vt_in, bool own) {
+11 -19
View File
@@ -323,14 +323,6 @@ size_t polysemous_inner_loop(
return n_pass_i;
}
struct Run_polysemous_inner_loop {
using T = size_t;
template <class HammingComputer, class... Types>
size_t f(Types... args) {
return polysemous_inner_loop<HammingComputer>(args...);
}
};
} // anonymous namespace
void IndexPQ::search_core_polysemous(
@@ -381,17 +373,17 @@ void IndexPQ::search_core_polysemous(
maxheap_heapify(k, heap_dis, heap_ids);
if (!generalized_hamming) {
Run_polysemous_inner_loop r;
n_pass += dispatch_HammingComputer(
pq.code_size,
r,
this,
dis_table_qi,
q_code,
k,
heap_dis,
heap_ids,
param_polysemous_ht);
n_pass += with_HammingComputer(
pq.code_size, [&]<class HammingComputer>() -> size_t {
return polysemous_inner_loop<HammingComputer>(
this,
dis_table_qi,
q_code,
k,
heap_dis,
heap_ids,
param_polysemous_ht);
});
} else { // generalized hamming
switch (pq.code_size) {
+12 -43
View File
@@ -332,30 +332,6 @@ void hamming_range_search(
}
}
struct Run_hammings_knn_hc {
using T = void;
template <class HammingComputer, class... Types>
void f(Types... args) {
hammings_knn_hc<HammingComputer>(args...);
}
};
struct Run_hammings_knn_mc {
using T = void;
template <class HammingComputer, class... Types>
void f(Types... args) {
hammings_knn_mc<HammingComputer>(args...);
}
};
struct Run_hamming_range_search {
using T = void;
template <class HammingComputer, class... Types>
void f(Types... args) {
hamming_range_search<HammingComputer>(args...);
}
};
} // namespace
/* Functions to maps vectors to bits. Assume proper allocation done beforehand,
@@ -511,19 +487,10 @@ void hammings_knn_hc(
int order,
ApproxTopK_mode_t approx_topk_mode,
const faiss::IDSelector* sel) {
Run_hammings_knn_hc r;
dispatch_HammingComputer(
ncodes,
r,
ncodes,
ha,
a,
b,
nb,
order,
true,
approx_topk_mode,
sel);
with_HammingComputer(ncodes, [&]<class HammingComputer>() {
hammings_knn_hc<HammingComputer>(
ncodes, ha, a, b, nb, order, true, approx_topk_mode, sel);
});
}
void hammings_knn_mc(
@@ -536,9 +503,10 @@ void hammings_knn_mc(
int32_t* __restrict distances,
int64_t* __restrict labels,
const faiss::IDSelector* sel) {
Run_hammings_knn_mc r;
dispatch_HammingComputer(
ncodes, r, ncodes, a, b, na, nb, k, distances, labels, sel);
with_HammingComputer(ncodes, [&]<class HammingComputer>() {
hammings_knn_mc<HammingComputer>(
ncodes, a, b, na, nb, k, distances, labels, sel);
});
}
void hamming_range_search(
@@ -550,9 +518,10 @@ void hamming_range_search(
size_t code_size,
RangeSearchResult* result,
const faiss::IDSelector* sel) {
Run_hamming_range_search r;
dispatch_HammingComputer(
code_size, r, a, b, na, nb, radius, code_size, result, sel);
with_HammingComputer(code_size, [&]<class HammingComputer>() {
hamming_range_search<HammingComputer>(
a, b, na, nb, radius, code_size, result, sel);
});
}
/* Count number of matches given a max threshold */
+18 -19
View File
@@ -56,30 +56,29 @@ SPECIALIZED_HC(64);
#undef SPECIALIZED_HC
/***************************************************************************
* Dispatching function that takes a code size and a consumer object
* the consumer object should contain a retun type t and a operation template
* function f() that must be called to perform the operation.
* Dispatching function that takes a code size and a C++20 template lambda.
* The lambda is called with the appropriate HammingComputer type:
* with_HammingComputer(code_size, [&]<class HammingComputer>() { ... });
**************************************************************************/
template <class Consumer, class... Types>
typename Consumer::T dispatch_HammingComputer(
int code_size,
Consumer& consumer,
Types... args) {
template <class F>
decltype(auto) with_HammingComputer(int code_size, F&& f) {
switch (code_size) {
#define DISPATCH_HC(CODE_SIZE) \
case CODE_SIZE: \
return consumer.template f<HammingComputer##CODE_SIZE>(args...);
DISPATCH_HC(4);
DISPATCH_HC(8);
DISPATCH_HC(16);
DISPATCH_HC(20);
DISPATCH_HC(32);
DISPATCH_HC(64);
case 4:
return f.template operator()<HammingComputer4>();
case 8:
return f.template operator()<HammingComputer8>();
case 16:
return f.template operator()<HammingComputer16>();
case 20:
return f.template operator()<HammingComputer20>();
case 32:
return f.template operator()<HammingComputer32>();
case 64:
return f.template operator()<HammingComputer64>();
default:
return consumer.template f<HammingComputerDefault>(args...);
return f.template operator()<HammingComputerDefault>();
}
#undef DISPATCH_HC
}
} // namespace faiss