mirror of
https://github.com/facebookresearch/faiss.git
synced 2026-10-11 22:50:00 +00:00
Summary: Pull Request resolved: https://github.com/facebookresearch/faiss/pull/4923 Convert HeapWithBuckets and the accum_and_*_tab functions from compile-time #ifdef __AVX2__ / __aarch64__ guards to SIMDLevel-templatized dynamic dispatch, consistent with the rest of FAISS's DD infrastructure (with_simd_level, simdlib, per-ISA .cpp files). Migration plan: P2234192040 Key changes: - New approx_topk.h: merges mode.h + heap_with_buckets.h. HeapWithBuckets<C,NB,N> dispatches via with_simd_level_256bit; SIMDLevel::NONE uses generic.h scalar fallback. SIMD primary template declares bs_addn without defining it. - New simdlib256-inl.h: out-of-line definition of HeapWithBucketsCMaxFloat::bs_addn using simdlib types (simd8float32_tpl/simd8uint32_tpl). Only included by per-ISA .cpp files. - New rq_beam_search_tab.h / rq_beam_search_tab-inl.h: declarations and definitions of accum_and_store/add/finalize_tab templatized on SIMDLevel, extracted from residual_quantizer_encode_steps.cpp. - New avx2.cpp / neon.cpp: explicit template instantiations per ISA. - residual_quantizer_encode_steps.cpp: HANDLE_APPROX / ACCUM_AND_*_TAB macros replaced with templatized lambdas + with_simd_level_256bit dispatch. Accumulation logic decomposed into compute_cent_distances_baseline and compute_cent_distances_simd<SL>. - Deleted: avx2-inl.h, mode.h, heap_with_buckets.h (merged into approx_topk.h). - Moved: approx_topk directory from utils/ to impl/. Reviewed By: algoriddle Differential Revision: D96360821 fbshipit-source-id: a1facb34b8af065e0a1430897cc05fbe763d3e26
146 lines
5.3 KiB
C++
146 lines
5.3 KiB
C++
/*
|
|
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
*
|
|
* This source code is licensed under the MIT license found in the
|
|
* LICENSE file in the root directory of this source tree.
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
#include <algorithm>
|
|
#include <limits>
|
|
#include <utility>
|
|
|
|
#include <faiss/impl/FaissAssert.h>
|
|
#include <faiss/utils/Heap.h>
|
|
|
|
namespace faiss {
|
|
|
|
// Scalar (generic) implementation of HeapWithBuckets.
|
|
// This is correct but slow because a compiler is unable to
|
|
// vectorize it properly. Used as the SIMDLevel::NONE fallback.
|
|
|
|
template <uint32_t NBUCKETS, uint32_t N>
|
|
struct HeapWithBucketsGenericCMaxFloat {
|
|
static void addn(
|
|
// number of elements
|
|
const uint32_t n,
|
|
// distances. It is assumed to have n elements.
|
|
const float* const __restrict distances,
|
|
// number of best elements to keep
|
|
const uint32_t k,
|
|
// output distances
|
|
float* const __restrict bh_val,
|
|
// output indices, each being within [0, n) range
|
|
int32_t* const __restrict bh_ids) {
|
|
// forward a call to bs_addn with 1 beam
|
|
bs_addn(1, n, distances, k, bh_val, bh_ids);
|
|
}
|
|
|
|
static void bs_addn(
|
|
// beam_size parameter of Beam Search algorithm
|
|
const uint32_t beam_size,
|
|
// number of elements per beam
|
|
const uint32_t n_per_beam,
|
|
// distances. It is assumed to have (n_per_beam * beam_size)
|
|
// elements.
|
|
const float* const __restrict distances,
|
|
// number of best elements to keep
|
|
const uint32_t k,
|
|
// output distances
|
|
float* const __restrict bh_val,
|
|
// output indices, each being within [0, n_per_beam * beam_size)
|
|
// range
|
|
int32_t* const __restrict bh_ids) {
|
|
// // Basically, the function runs beam_size iterations.
|
|
// // Every iteration NBUCKETS * N elements are added to a regular heap.
|
|
// // So, maximum number of added elements is beam_size * NBUCKETS * N.
|
|
// // This number is expected to be less or equal than k.
|
|
// FAISS_THROW_IF_NOT_FMT(
|
|
// beam_size * NBUCKETS * N >= k,
|
|
// "Cannot pick %d elements, only %d. "
|
|
// "Check the function and template arguments values.",
|
|
// k,
|
|
// beam_size * NBUCKETS * N);
|
|
|
|
using C = CMax<float, int>;
|
|
|
|
// main loop
|
|
for (uint32_t beam_index = 0; beam_index < beam_size; beam_index++) {
|
|
float min_distances_i[N][NBUCKETS];
|
|
int min_indices_i[N][NBUCKETS];
|
|
|
|
for (uint32_t p = 0; p < N; p++) {
|
|
for (uint32_t j = 0; j < NBUCKETS; j++) {
|
|
min_distances_i[p][j] = std::numeric_limits<float>::max();
|
|
min_indices_i[p][j] = 0;
|
|
}
|
|
}
|
|
|
|
const uint32_t nb = (n_per_beam / NBUCKETS) * NBUCKETS;
|
|
|
|
// put the data into buckets
|
|
for (uint32_t ip = 0; ip < nb; ip += NBUCKETS) {
|
|
for (uint32_t j = 0; j < NBUCKETS; j++) {
|
|
const int index = j + ip + n_per_beam * beam_index;
|
|
const float distance = distances[index];
|
|
|
|
int index_candidate = index;
|
|
float distance_candidate = distance;
|
|
|
|
for (uint32_t p = 0; p < N; p++) {
|
|
if (distance_candidate < min_distances_i[p][j]) {
|
|
std::swap(
|
|
distance_candidate, min_distances_i[p][j]);
|
|
std::swap(index_candidate, min_indices_i[p][j]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// merge every bucket into the regular heap
|
|
for (uint32_t p = 0; p < N; p++) {
|
|
for (uint32_t j = 0; j < NBUCKETS; j++) {
|
|
// this exact way is needed to maintain the order as if the
|
|
// input elements were pushed to the heap sequentially
|
|
|
|
if (C::cmp2(bh_val[0],
|
|
min_distances_i[p][j],
|
|
bh_ids[0],
|
|
min_indices_i[p][j])) {
|
|
heap_replace_top<C>(
|
|
k,
|
|
bh_val,
|
|
bh_ids,
|
|
min_distances_i[p][j],
|
|
min_indices_i[p][j]);
|
|
}
|
|
}
|
|
}
|
|
|
|
// process leftovers
|
|
for (uint32_t ip = nb; ip < n_per_beam; ip++) {
|
|
const int32_t index = ip + n_per_beam * beam_index;
|
|
const float value = distances[index];
|
|
|
|
if (C::cmp(bh_val[0], value)) {
|
|
heap_replace_top<C>(k, bh_val, bh_ids, value, index);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
// Legacy name kept for backward compatibility (used when
|
|
// approx_topk.h is not included).
|
|
template <typename C, uint32_t NBUCKETS, uint32_t N>
|
|
struct HeapWithBucketsGeneric {
|
|
// not implemented for arbitrary C
|
|
};
|
|
|
|
template <uint32_t NBUCKETS, uint32_t N>
|
|
struct HeapWithBucketsGeneric<CMax<float, int>, NBUCKETS, N>
|
|
: HeapWithBucketsGenericCMaxFloat<NBUCKETS, N> {};
|
|
|
|
} // namespace faiss
|