mirror of
https://github.com/facebookresearch/faiss.git
synced 2026-10-12 02:13:36 +00:00
Templatize search_from_candidate_unbounded for VisitedTable devirtualization (#5270)
Summary: Pull Request resolved: https://github.com/facebookresearch/faiss/pull/5270 Apply the VisitedTable templatization pattern to search_from_candidate_unbounded in HNSW.cpp, following the same approach already used for search_from_candidates_fixVT and search_neighbors_to_add_fixVT. Changes: - Renamed search_from_candidate_unbounded_impl<C> to search_from_candidate_unbounded_fixVT<VTType, C> - Changed VT parameter from VisitedTable* to VTType& reference - Changed vt->set() and vt->prefetch() to vt.set() and vt.prefetch() for inlining via template instantiation - Merged the dispatch logic directly into hnsw_detail::search_from_candidate_unbounded and inlined the dispatch pattern in search_impl<C> for similarity mode - This eliminates the intermediate search_from_candidate_unbounded_dispatch template function, keeping the dispatch logic local to each call site This eliminates virtual dispatch overhead in the hot HNSW graph-traversal loop for Unicorn's IVF-HNSW coarse-quantizer search path, addressing the +9.5% regression identified in P2359940488. Differential Revision: D107365487 fbshipit-source-id: eba7e461c4831616ae6500c4696930705cde9dbe
This commit is contained in:
committed by
meta-codesync[bot]
parent
3cff0f419c
commit
e60baeb7b6
+34
-12
@@ -1351,13 +1351,13 @@ void reservePriorityQueue(
|
||||
/// Templated body of `search_from_candidate_unbounded`. The choice of
|
||||
/// max-heap vs min-heap for both `top_candidates` and `candidates` is
|
||||
/// derived from C via `TopCandidatesQueue` / `CandidatesQueue`.
|
||||
template <class C>
|
||||
TopCandidatesQueue<C> search_from_candidate_unbounded_impl(
|
||||
template <typename VTType, class C>
|
||||
TopCandidatesQueue<C> search_from_candidate_unbounded_fixVT(
|
||||
const HNSW& hnsw,
|
||||
const HNSW::Node& node,
|
||||
DistanceComputer& qdis,
|
||||
int ef,
|
||||
VisitedTable* vt,
|
||||
VTType& vt,
|
||||
HNSWStats& stats) {
|
||||
int ndis = 0;
|
||||
TopCandidatesQueue<C> top_candidates;
|
||||
@@ -1369,7 +1369,7 @@ TopCandidatesQueue<C> search_from_candidate_unbounded_impl(
|
||||
top_candidates.push(node);
|
||||
candidates.push(node);
|
||||
|
||||
vt->set(node.second);
|
||||
vt.set(node.second);
|
||||
|
||||
while (!candidates.empty()) {
|
||||
float d0;
|
||||
@@ -1394,7 +1394,7 @@ TopCandidatesQueue<C> search_from_candidate_unbounded_impl(
|
||||
break;
|
||||
}
|
||||
|
||||
vt->prefetch(v1);
|
||||
vt.prefetch(v1);
|
||||
jmax += 1;
|
||||
}
|
||||
|
||||
@@ -1417,7 +1417,7 @@ TopCandidatesQueue<C> search_from_candidate_unbounded_impl(
|
||||
int v1 = hnsw.neighbors[j];
|
||||
|
||||
saved_j[counter] = v1;
|
||||
counter += vt->set(v1) ? 1 : 0;
|
||||
counter += vt.set(v1) ? 1 : 0;
|
||||
|
||||
if (counter == 4) {
|
||||
float dis[4];
|
||||
@@ -1464,8 +1464,8 @@ TopCandidatesQueue<C> search_from_candidate_unbounded_impl(
|
||||
|
||||
/// Public dispatcher: only the distance (CMax) flavor is exposed because
|
||||
/// its return type — `std::priority_queue<HNSW::Node>` — is the CMax
|
||||
/// max-heap. Internal callers that need similarity mode use
|
||||
/// `search_from_candidate_unbounded_impl<C_similarity>` directly.
|
||||
/// max-heap. Internal callers that need similarity mode use the same
|
||||
/// dispatch pattern inline.
|
||||
std::priority_queue<HNSW::Node> hnsw_detail::search_from_candidate_unbounded(
|
||||
const HNSW& hnsw,
|
||||
const HNSW::Node& node,
|
||||
@@ -1473,8 +1473,16 @@ std::priority_queue<HNSW::Node> hnsw_detail::search_from_candidate_unbounded(
|
||||
int ef,
|
||||
VisitedTable* vt,
|
||||
HNSWStats& stats) {
|
||||
return search_from_candidate_unbounded_impl<HNSW::C_distance>(
|
||||
hnsw, node, qdis, ef, vt, stats);
|
||||
using C = HNSW::C_distance;
|
||||
auto call = [&]<typename VTType>(VTType& vt_concrete) {
|
||||
return search_from_candidate_unbounded_fixVT<VTType, C>(
|
||||
hnsw, node, qdis, ef, vt_concrete, stats);
|
||||
};
|
||||
if (VisitedTableVector* vtv = dynamic_cast<VisitedTableVector*>(vt)) {
|
||||
return call(*vtv);
|
||||
}
|
||||
VisitedTableSet& vts = dynamic_cast<VisitedTableSet&>(*vt);
|
||||
return call(vts);
|
||||
}
|
||||
|
||||
namespace {
|
||||
@@ -1555,8 +1563,22 @@ HNSWStats search_impl(
|
||||
}
|
||||
}
|
||||
} else {
|
||||
auto top_candidates = search_from_candidate_unbounded_impl<C>(
|
||||
hnsw, HNSW::Node(d_nearest, nearest), qdis, ef, &vt, stats);
|
||||
auto call = [&]<typename VTType>(VTType& vt_concrete) {
|
||||
return search_from_candidate_unbounded_fixVT<VTType, C>(
|
||||
hnsw,
|
||||
HNSW::Node(d_nearest, nearest),
|
||||
qdis,
|
||||
ef,
|
||||
vt_concrete,
|
||||
stats);
|
||||
};
|
||||
TopCandidatesQueue<C> top_candidates;
|
||||
if (VisitedTableVector* vtv = dynamic_cast<VisitedTableVector*>(&vt)) {
|
||||
top_candidates = call(*vtv);
|
||||
} else {
|
||||
VisitedTableSet& vts = dynamic_cast<VisitedTableSet&>(vt);
|
||||
top_candidates = call(vts);
|
||||
}
|
||||
|
||||
while (top_candidates.size() > static_cast<size_t>(k)) {
|
||||
top_candidates.pop();
|
||||
|
||||
Reference in New Issue
Block a user