Add FastScanCodeScanner dispatch boundary with per-SIMD TUs (#4897)

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

Add `FastScanCodeScanner`, a virtual base that bundles handler + kernel
behind the SIMD dispatch boundary. In DD mode, `SINGLE_SIMD_LEVEL = NONE`
so the existing fast scan code path uses emulated SIMD types. The new
scanner provides per-SIMD translation units (AVX2, AVX512, ARM_NEON)
compiled with the correct ISA flags, and a factory function
(`make_fast_scan_knn_scanner`) that uses `DISPATCH_SIMDLevel` to select
the right TU at runtime.

This follows the proven `THE_LEVEL_TO_DISPATCH` pattern from the scalar
quantizer per-SIMD TUs (`sq-dispatch.h`). Each per-SIMD TU includes
`dispatching.h` which provides:
- `ScannerMixIn<Handler>`: wraps a concrete handler and calls accumulation
  kernels (both search_1 multi-BB and QBS paths)
- Factory specialization `make_fast_scan_scanner_impl<SL>()` with
  combinatorial dispatch over `is_max × with_id_map × handler_type`
  (SingleResultHandler for k=1, HeapHandler for k≤20, ReservoirHandler
  for k>20)

New files:
- `impl/fast_scan/dispatching.h` — dispatch template header
- `impl/fast_scan/impl-avx2.cpp` — AVX2 per-SIMD TU
- `impl/fast_scan/impl-avx512.cpp` — AVX512 per-SIMD TU
- `impl/fast_scan/impl-neon.cpp` — ARM NEON TU (with ARM_SVE forwarding)

Modified files:
- `impl/fast_scan/pq4_fast_scan.h` — FastScanCodeScanner base + factory decl
- `impl/fast_scan/pq4_fast_scan.cpp` — NONE specialization + dispatch wrapper
- `xplat.bzl` / `CMakeLists.txt` — register SIMD files and header

Note: RaBitQ handler is not wired through FastScanCodeScanner in this
diff. That comes in later diffs when callers are switched.

Reviewed By: mdouze

Differential Revision: D95950483

fbshipit-source-id: 03841671f59c0d99de7d0c7408a64e7a8383a8e7
This commit is contained in:
Gergely Szilvasy
2026-03-12 02:15:42 -07:00
committed by meta-codesync[bot]
parent 5b83ec6856
commit 4a0fba1fe2
21 changed files with 590 additions and 76 deletions
+7 -2
View File
@@ -9,16 +9,19 @@
# Architecture-specific: only include files for the current build architecture
# =============================================================================
set(FAISS_SIMD_AVX2_SRC
impl/fast_scan/impl-avx2.cpp
impl/pq_code_distance/pq_code_distance-avx2.cpp
impl/scalar_quantizer/sq-avx2.cpp
utils/simd_impl/distances_avx2.cpp
)
set(FAISS_SIMD_AVX512_SRC
impl/fast_scan/impl-avx512.cpp
impl/pq_code_distance/pq_code_distance-avx512.cpp
impl/scalar_quantizer/sq-avx512.cpp
utils/simd_impl/distances_avx512.cpp
)
set(FAISS_SIMD_NEON_SRC
impl/fast_scan/impl-neon.cpp
impl/scalar_quantizer/sq-neon.cpp
utils/simd_impl/distances_aarch64.cpp
)
@@ -117,7 +120,7 @@ set(FAISS_SRC
impl/kmeans1d.cpp
impl/lattice_Zn.cpp
impl/mapped_io.cpp
impl/fast_scan/pq4_fast_scan.cpp
impl/fast_scan/fast_scan.cpp
impl/fast_scan/pq4_fast_scan_search_1.cpp
impl/fast_scan/pq4_fast_scan_search_qbs.cpp
impl/residual_quantizer_encode_steps.cpp
@@ -262,7 +265,9 @@ set(FAISS_HEADERS
impl/kmeans1d.h
impl/lattice_Zn.h
impl/platform_macros.h
impl/fast_scan/pq4_fast_scan.h
impl/fast_scan/accumulate_loops.h
impl/fast_scan/dispatching.h
impl/fast_scan/fast_scan.h
impl/fast_scan/decompose_qbs.h
impl/fast_scan/kernels_simd256.h
impl/fast_scan/kernels_simd512.h
+1 -1
View File
@@ -14,7 +14,7 @@
#include <faiss/impl/LocalSearchQuantizer.h>
#include <faiss/impl/ResidualQuantizer.h>
#include <faiss/impl/fast_scan/FastScanDistancePostProcessing.h>
#include <faiss/impl/fast_scan/pq4_fast_scan.h>
#include <faiss/impl/fast_scan/fast_scan.h>
#include <faiss/utils/quantize_lut.h>
#include <faiss/utils/utils.h>
+1 -1
View File
@@ -16,7 +16,7 @@
#include <faiss/impl/IDSelector.h>
#include <faiss/impl/RaBitQUtils.h>
#include <faiss/impl/fast_scan/FastScanDistancePostProcessing.h>
#include <faiss/impl/fast_scan/pq4_fast_scan.h>
#include <faiss/impl/fast_scan/fast_scan.h>
#include <faiss/impl/fast_scan/simd_result_handlers.h>
#include <faiss/utils/hamming.h>
#include <faiss/utils/quantize_lut.h>
+1 -1
View File
@@ -15,7 +15,7 @@
#include <faiss/impl/AuxIndexStructures.h>
#include <faiss/impl/FaissAssert.h>
#include <faiss/impl/fast_scan/FastScanDistancePostProcessing.h>
#include <faiss/impl/fast_scan/pq4_fast_scan.h>
#include <faiss/impl/fast_scan/fast_scan.h>
#include <faiss/impl/simd_dispatch.h>
#include <faiss/invlists/BlockInvertedLists.h>
#include <faiss/utils/distances.h>
+1 -1
View File
@@ -19,7 +19,7 @@
#include <faiss/impl/FaissAssert.h>
#include <faiss/impl/RaBitQUtils.h>
#include <faiss/impl/fast_scan/FastScanDistancePostProcessing.h>
#include <faiss/impl/fast_scan/pq4_fast_scan.h>
#include <faiss/impl/fast_scan/fast_scan.h>
#include <faiss/impl/fast_scan/simd_result_handlers.h>
#include <faiss/invlists/BlockInvertedLists.h>
#include <faiss/utils/hamming.h>
+1 -1
View File
@@ -22,7 +22,7 @@
#include <faiss/invlists/BlockInvertedLists.h>
#include <faiss/impl/fast_scan/pq4_fast_scan.h>
#include <faiss/impl/fast_scan/fast_scan.h>
#include <faiss/impl/fast_scan/simd_result_handlers.h>
namespace faiss {
+1 -1
View File
@@ -17,7 +17,7 @@
#include <faiss/impl/RaBitQUtils.h>
#include <faiss/impl/RaBitQuantizerMultiBit.h>
#include <faiss/impl/fast_scan/FastScanDistancePostProcessing.h>
#include <faiss/impl/fast_scan/pq4_fast_scan.h>
#include <faiss/impl/fast_scan/fast_scan.h>
#include <faiss/impl/fast_scan/simd_result_handlers.h>
#include <faiss/invlists/BlockInvertedLists.h>
#include <faiss/utils/distances.h>
+1 -1
View File
@@ -10,7 +10,7 @@
#include <memory>
#include <faiss/impl/fast_scan/FastScanDistancePostProcessing.h>
#include <faiss/impl/fast_scan/pq4_fast_scan.h>
#include <faiss/impl/fast_scan/fast_scan.h>
#include <faiss/utils/utils.h>
namespace faiss {
+1 -1
View File
@@ -10,7 +10,7 @@
#include <faiss/impl/RaBitQUtils.h>
#include <faiss/impl/RaBitQuantizerMultiBit.h>
#include <faiss/impl/fast_scan/FastScanDistancePostProcessing.h>
#include <faiss/impl/fast_scan/pq4_fast_scan.h>
#include <faiss/impl/fast_scan/fast_scan.h>
#include <faiss/utils/utils.h>
#include <algorithm>
#include <cmath>
+1 -1
View File
@@ -6,7 +6,7 @@
*/
#include <faiss/impl/CodePackerRaBitQ.h>
#include <faiss/impl/fast_scan/pq4_fast_scan.h>
#include <faiss/impl/fast_scan/fast_scan.h>
#include <cstring>
+226
View File
@@ -0,0 +1,226 @@
/*
* 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
/**
* @file accumulate_loops.h
* @brief Shared accumulation loop helpers for fast-scan search paths.
*
* Contains:
* - accumulate_fixed_blocks / pq4_accumulate_loop_fixed_scaler
* (search_1 multi-BB path, bbs > 32)
* - accumulate_q_4step_256 / pq4_accumulate_loop_qbs_fixed_scaler_256
* (QBS path, bbs == 32, 256-bit kernel only)
*
* The QBS helpers use pq4_kernel_qbs_256 exclusively (not decompose_qbs.h)
* because decompose_qbs.h includes kernels_simd512.h which uses 512-bit
* types that are empty primary templates when SINGLE_SIMD_LEVEL=NONE
* (DD mode). SL-parameterizing the 512-bit kernels is future work.
*
* All functions live in `namespace faiss` (not anonymous) so they can be
* shared by both the per-SIMD TU dispatcher (dispatching.h) and the old
* free-function search paths (pq4_fast_scan_search_1.cpp).
*
* The QBS helpers here always use pq4_kernel_qbs_256 (never 512-bit).
* This is required for the per-SIMD DD TUs where SINGLE_SIMD_LEVEL=NONE
* leaves 512-bit types empty. The old pq4_fast_scan_search_qbs.cpp
* continues to use decompose_qbs.h which includes both 256 and 512 paths.
*/
#include <cassert>
#include <faiss/impl/FaissAssert.h>
#include <faiss/impl/fast_scan/LookupTableScaler.h>
#include <faiss/impl/fast_scan/kernels_simd256.h>
#include <faiss/impl/fast_scan/simd_result_handlers.h>
namespace faiss {
using namespace simd_result_handlers;
/***************************************************************
* Search_1 path helpers (multi-BB kernel, bbs > 32)
***************************************************************/
template <int NQ, int BB, class ResultHandler, class Scaler>
void accumulate_fixed_blocks(
size_t nb,
int nsq,
const uint8_t* codes,
const uint8_t* LUT,
ResultHandler& res,
const Scaler& scaler,
size_t block_stride) {
constexpr int bbs = 32 * BB;
for (size_t j0 = 0; j0 < nb; j0 += bbs) {
FixedStorageHandler<NQ, 2 * BB> res2;
kernel_accumulate_block<NQ, BB>(nsq, codes, LUT, res2, scaler);
res.set_block_origin(0, j0);
res2.to_other_handler(res);
codes += block_stride;
}
}
template <class ResultHandler, class Scaler>
void pq4_accumulate_loop_fixed_scaler(
int nq,
size_t nb,
int bbs,
int nsq,
const uint8_t* codes,
const uint8_t* LUT,
ResultHandler& res,
const Scaler& scaler,
size_t block_stride) {
FAISS_THROW_IF_NOT(is_aligned_pointer(codes));
FAISS_THROW_IF_NOT(is_aligned_pointer(LUT));
FAISS_THROW_IF_NOT(bbs % 32 == 0);
FAISS_THROW_IF_NOT(nb % bbs == 0);
#define FAISS_ACCLOOP_DISPATCH(NQ, BB) \
case NQ * 1000 + BB: \
accumulate_fixed_blocks<NQ, BB>( \
nb, nsq, codes, LUT, res, scaler, block_stride); \
break
switch (nq * 1000 + bbs / 32) {
FAISS_ACCLOOP_DISPATCH(1, 1);
FAISS_ACCLOOP_DISPATCH(1, 2);
FAISS_ACCLOOP_DISPATCH(1, 3);
FAISS_ACCLOOP_DISPATCH(1, 4);
FAISS_ACCLOOP_DISPATCH(1, 5);
FAISS_ACCLOOP_DISPATCH(2, 1);
FAISS_ACCLOOP_DISPATCH(2, 2);
FAISS_ACCLOOP_DISPATCH(3, 1);
FAISS_ACCLOOP_DISPATCH(4, 1);
default:
FAISS_THROW_FMT("nq=%d bbs=%d not instantiated", nq, bbs);
}
#undef FAISS_ACCLOOP_DISPATCH
}
/***************************************************************
* QBS path helpers (bbs == 32, 256-bit kernel only)
***************************************************************/
template <int QBS, class ResultHandler, class Scaler>
void accumulate_q_4step_256(
size_t ntotal2,
int nsq,
const uint8_t* codes,
const uint8_t* LUT0,
ResultHandler& res,
const Scaler& scaler,
size_t block_stride) {
constexpr int Q1 = QBS & 15;
constexpr int Q2 = (QBS >> 4) & 15;
constexpr int Q3 = (QBS >> 8) & 15;
constexpr int Q4 = (QBS >> 12) & 15;
constexpr int SQ = Q1 + Q2 + Q3 + Q4;
for (size_t j0 = 0; j0 < ntotal2; j0 += 32) {
FixedStorageHandler<SQ, 2> res2;
const uint8_t* LUT = LUT0;
pq4_kernel_qbs_256<Q1>(nsq, codes, LUT, res2, scaler);
LUT += Q1 * nsq * 16;
if (Q2 > 0) {
res2.set_block_origin(Q1, 0);
pq4_kernel_qbs_256<Q2>(nsq, codes, LUT, res2, scaler);
LUT += Q2 * nsq * 16;
}
if (Q3 > 0) {
res2.set_block_origin(Q1 + Q2, 0);
pq4_kernel_qbs_256<Q3>(nsq, codes, LUT, res2, scaler);
LUT += Q3 * nsq * 16;
}
if (Q4 > 0) {
res2.set_block_origin(Q1 + Q2 + Q3, 0);
pq4_kernel_qbs_256<Q4>(nsq, codes, LUT, res2, scaler);
}
res.set_block_origin(0, j0);
res2.to_other_handler(res);
codes += block_stride;
}
}
template <class ResultHandler, class Scaler>
void pq4_accumulate_loop_qbs_fixed_scaler_256(
int qbs,
size_t ntotal2,
int nsq,
const uint8_t* codes,
const uint8_t* LUT0,
ResultHandler& res,
const Scaler& scaler,
size_t block_stride) {
assert(nsq % 2 == 0);
assert(is_aligned_pointer(codes));
assert(is_aligned_pointer(LUT0));
switch (qbs) {
#define FAISS_QBS256_DISPATCH(QBS) \
case QBS: \
accumulate_q_4step_256<QBS>( \
ntotal2, nsq, codes, LUT0, res, scaler, block_stride); \
return;
FAISS_QBS256_DISPATCH(0x3333); // 12
FAISS_QBS256_DISPATCH(0x2333); // 11
FAISS_QBS256_DISPATCH(0x2233); // 10
FAISS_QBS256_DISPATCH(0x333); // 9
FAISS_QBS256_DISPATCH(0x2223); // 9
FAISS_QBS256_DISPATCH(0x233); // 8
FAISS_QBS256_DISPATCH(0x1223); // 8
FAISS_QBS256_DISPATCH(0x223); // 7
FAISS_QBS256_DISPATCH(0x34); // 7
FAISS_QBS256_DISPATCH(0x133); // 7
FAISS_QBS256_DISPATCH(0x6); // 6
FAISS_QBS256_DISPATCH(0x33); // 6
FAISS_QBS256_DISPATCH(0x123); // 6
FAISS_QBS256_DISPATCH(0x222); // 6
FAISS_QBS256_DISPATCH(0x23); // 5
FAISS_QBS256_DISPATCH(0x5); // 5
FAISS_QBS256_DISPATCH(0x13); // 4
FAISS_QBS256_DISPATCH(0x22); // 4
FAISS_QBS256_DISPATCH(0x4); // 4
FAISS_QBS256_DISPATCH(0x3); // 3
FAISS_QBS256_DISPATCH(0x21); // 3
FAISS_QBS256_DISPATCH(0x2); // 2
FAISS_QBS256_DISPATCH(0x1); // 1
#undef FAISS_QBS256_DISPATCH
}
// Default: qbs not known at compile time
for (size_t j0 = 0; j0 < ntotal2; j0 += 32) {
const uint8_t* LUT = LUT0;
int qi = qbs;
int i0 = 0;
while (qi) {
int nq = qi & 15;
qi >>= 4;
res.set_block_origin(i0, j0);
#define FAISS_NQ256_DISPATCH(NQ) \
case NQ: \
pq4_kernel_qbs_256<NQ>(nsq, codes, LUT, res, scaler); \
break
switch (nq) {
FAISS_NQ256_DISPATCH(1);
FAISS_NQ256_DISPATCH(2);
FAISS_NQ256_DISPATCH(3);
FAISS_NQ256_DISPATCH(4);
#undef FAISS_NQ256_DISPATCH
default:
FAISS_THROW_FMT("accumulate nq=%d not instantiated", nq);
}
i0 += nq;
LUT += nq * nsq * 16;
}
codes += block_stride;
}
}
} // namespace faiss
+169
View File
@@ -0,0 +1,169 @@
/*
* 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
/**
* @file dispatching.h
* @brief Per-SIMD TU dispatch template for fast scan.
*
* This header is included once per SIMD TU with THE_LEVEL_TO_DISPATCH
* set to the desired SIMDLevel. It provides:
* - ScannerMixIn: wraps a handler + calls kernel at the TU's SIMD level
* - make_fast_scan_scanner_impl<SL>: factory specialization
*
* Usage (in a per-SIMD .cpp file):
* #define THE_LEVEL_TO_DISPATCH SIMDLevel::AVX2
* #include <faiss/impl/fast_scan/dispatching.h>
*
* Kernel helpers come from accumulate_loops.h (search_1 multi-BB path
* and QBS 256-bit path). The QBS helpers use pq4_kernel_qbs_256 only
* because decompose_qbs.h pulls in 512-bit types that fail with
* SINGLE_SIMD_LEVEL=NONE in DD mode.
*/
#ifndef THE_LEVEL_TO_DISPATCH
#error "Define THE_LEVEL_TO_DISPATCH before including this header"
#endif
#include <memory>
#include <faiss/impl/fast_scan/accumulate_loops.h>
#include <faiss/impl/fast_scan/fast_scan.h>
namespace faiss {
using namespace simd_result_handlers;
/***************************************************************
* ScannerMixIn: wraps a concrete handler + calls accumulation
* kernels. Lives behind the virtual FastScanCodeScanner interface
* so callers don't need to know the handler type.
***************************************************************/
template <class Handler>
struct ScannerMixIn : FastScanCodeScanner {
Handler handler_;
template <typename... Args>
explicit ScannerMixIn(Args&&... args)
: handler_(std::forward<Args>(args)...) {}
SIMDResultHandlerToFloat* handler() override {
return &handler_;
}
void accumulate_loop(
int nq,
size_t nb,
int bbs,
int nsq,
const uint8_t* codes,
const uint8_t* LUT,
int pq2x4_scale,
size_t block_stride) override {
if (pq2x4_scale) {
NormTableScaler<> scaler(pq2x4_scale);
pq4_accumulate_loop_fixed_scaler(
nq,
nb,
bbs,
nsq,
codes,
LUT,
handler_,
scaler,
block_stride);
} else {
DummyScaler<> dummy;
pq4_accumulate_loop_fixed_scaler(
nq,
nb,
bbs,
nsq,
codes,
LUT,
handler_,
dummy,
block_stride);
}
}
void accumulate_loop_qbs(
int qbs,
size_t nb,
int nsq,
const uint8_t* codes,
const uint8_t* LUT,
int pq2x4_scale,
size_t block_stride) override {
if (pq2x4_scale) {
NormTableScaler<> scaler(pq2x4_scale);
pq4_accumulate_loop_qbs_fixed_scaler_256(
qbs, nb, nsq, codes, LUT, handler_, scaler, block_stride);
} else {
DummyScaler<> dummy;
pq4_accumulate_loop_qbs_fixed_scaler_256(
qbs, nb, nsq, codes, LUT, handler_, dummy, block_stride);
}
}
};
/***************************************************************
* Factory specialization for this SIMD level.
*
* Combinatorial dispatch: is_max × with_id_map × handler type
* k == 1: SingleResultHandler
* impl even: HeapHandler
* impl odd: ReservoirHandler (capacity = 2*k)
***************************************************************/
template <>
std::unique_ptr<FastScanCodeScanner> make_fast_scan_scanner_impl<
THE_LEVEL_TO_DISPATCH>(
bool is_max,
int impl,
size_t nq,
size_t ntotal,
int64_t k,
float* distances,
int64_t* ids,
const IDSelector* sel,
bool with_id_map) {
// Helper lambda: given comparator C and with_id_map W, select handler
auto make = [&]<class C, bool W>() -> std::unique_ptr<FastScanCodeScanner> {
if (k == 1) {
using H = SingleResultHandler<C, W>;
return std::make_unique<ScannerMixIn<H>>(
nq, ntotal, distances, ids, sel);
} else if (impl % 2 == 0) {
using H = HeapHandler<C, W>;
return std::make_unique<ScannerMixIn<H>>(
nq, ntotal, k, distances, ids, sel);
} else {
using H = ReservoirHandler<C, W>;
return std::make_unique<ScannerMixIn<H>>(
nq, ntotal, size_t(k), size_t(2 * k), distances, ids, sel);
}
};
if (is_max) {
if (with_id_map) {
return make.template operator()<CMax<uint16_t, int64_t>, true>();
} else {
return make.template operator()<CMax<uint16_t, int>, false>();
}
} else {
if (with_id_map) {
return make.template operator()<CMin<uint16_t, int64_t>, true>();
} else {
return make.template operator()<CMin<uint16_t, int>, false>();
}
}
}
} // namespace faiss
@@ -6,8 +6,9 @@
*/
#include <faiss/impl/FaissAssert.h>
#include <faiss/impl/fast_scan/pq4_fast_scan.h>
#include <faiss/impl/fast_scan/fast_scan.h>
#include <faiss/impl/fast_scan/simd_result_handlers.h>
#include <faiss/impl/simd_dispatch.h>
#include <array>
@@ -351,3 +352,42 @@ int pq4_pack_LUT_qbs_q_map(
}
} // namespace faiss
/***************************************************************
* FastScanCodeScanner: NONE specialization + dispatch wrapper.
*
* The NONE specialization provides the scalar fallback.
* Per-SIMD specializations (AVX2, AVX512, ARM_NEON) are in
* impl-avx2.cpp, impl-avx512.cpp, impl-neon.cpp respectively.
***************************************************************/
#define THE_LEVEL_TO_DISPATCH SIMDLevel::NONE
#include <faiss/impl/fast_scan/dispatching.h> // IWYU pragma: keep
#undef THE_LEVEL_TO_DISPATCH
namespace faiss {
std::unique_ptr<FastScanCodeScanner> make_fast_scan_knn_scanner(
bool is_max,
int impl,
size_t nq,
size_t ntotal,
int64_t k,
float* distances,
int64_t* ids,
const IDSelector* sel,
bool with_id_map) {
DISPATCH_SIMDLevel(
make_fast_scan_scanner_impl,
is_max,
impl,
nq,
ntotal,
k,
distances,
ids,
sel,
with_id_map);
}
} // namespace faiss
@@ -9,8 +9,10 @@
#include <cstdint>
#include <cstdlib>
#include <memory>
#include <faiss/impl/CodePacker.h>
#include <faiss/utils/simd_levels.h>
/** PQ4 SIMD packing and accumulation functions
*
@@ -25,6 +27,8 @@
namespace faiss {
struct SIMDResultHandler;
struct SIMDResultHandlerToFloat;
struct IDSelector;
/** Pack codes for consumption by the SIMD kernels.
* The unused bytes are set to 0.
@@ -220,4 +224,68 @@ void accumulate_to_mem(
const uint8_t* LUT,
uint16_t* accu);
/***************************************************************
* FastScanCodeScanner: virtual base that bundles handler + kernel
* behind the SIMD dispatch boundary. Per-SIMD TUs instantiate this
* with the correct SIMDLevel so that handler and kernel share the
* same SIMD types.
***************************************************************/
struct FastScanCodeScanner {
virtual ~FastScanCodeScanner() = default;
/// Access the underlying result handler (for begin/end/normalizer calls)
virtual SIMDResultHandlerToFloat* handler() = 0;
/// Run the search_1 accumulation loop (bbs > 32, multi-BB kernel)
virtual void accumulate_loop(
int nq,
size_t nb,
int bbs,
int nsq,
const uint8_t* codes,
const uint8_t* LUT,
int pq2x4_scale,
size_t block_stride) = 0;
/// Run the QBS accumulation loop (bbs == 32)
virtual void accumulate_loop_qbs(
int qbs,
size_t nb,
int nsq,
const uint8_t* codes,
const uint8_t* LUT,
int pq2x4_scale,
size_t block_stride) = 0;
};
/// Per-SIMD factory: explicitly specialized in each per-SIMD TU
/// (impl-avx2.cpp, impl-avx512.cpp, impl-neon.cpp, fast_scan.cpp for NONE).
/// Not called directly — use make_fast_scan_knn_scanner() instead.
template <SIMDLevel SL>
std::unique_ptr<FastScanCodeScanner> make_fast_scan_scanner_impl(
bool is_max,
int impl,
size_t nq,
size_t ntotal,
int64_t k,
float* distances,
int64_t* ids,
const IDSelector* sel,
bool with_id_map);
/// Runtime dispatch wrapper: selects the best available SIMD level
/// (via DISPATCH_SIMDLevel) and delegates to the corresponding
/// make_fast_scan_scanner_impl<SL> specialization.
std::unique_ptr<FastScanCodeScanner> make_fast_scan_knn_scanner(
bool is_max,
int impl,
size_t nq,
size_t ntotal,
int64_t k,
float* distances,
int64_t* ids,
const IDSelector* sel,
bool with_id_map = false);
} // namespace faiss
+13
View File
@@ -0,0 +1,13 @@
/*
* 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.
*/
#ifdef COMPILE_SIMD_AVX2
#define THE_LEVEL_TO_DISPATCH SIMDLevel::AVX2
#include <faiss/impl/fast_scan/dispatching.h> // IWYU pragma: keep
#endif // COMPILE_SIMD_AVX2
+13
View File
@@ -0,0 +1,13 @@
/*
* 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.
*/
#ifdef COMPILE_SIMD_AVX512
#define THE_LEVEL_TO_DISPATCH SIMDLevel::AVX512
#include <faiss/impl/fast_scan/dispatching.h> // IWYU pragma: keep
#endif // COMPILE_SIMD_AVX512
+39
View File
@@ -0,0 +1,39 @@
/*
* 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.
*/
#ifdef COMPILE_SIMD_ARM_NEON
#define THE_LEVEL_TO_DISPATCH SIMDLevel::ARM_NEON
#include <faiss/impl/fast_scan/dispatching.h> // IWYU pragma: keep
// ARM_SVE: forward to ARM_NEON implementation until a dedicated SVE
// specialization is written (same pattern as scalar_quantizer/sq-neon.cpp).
#ifdef COMPILE_SIMD_ARM_SVE
namespace faiss {
template <>
std::unique_ptr<FastScanCodeScanner> make_fast_scan_scanner_impl<
SIMDLevel::ARM_SVE>(
bool is_max,
int impl,
size_t nq,
size_t ntotal,
int64_t k,
float* distances,
int64_t* ids,
const IDSelector* sel,
bool with_id_map) {
return make_fast_scan_scanner_impl<SIMDLevel::ARM_NEON>(
is_max, impl, nq, ntotal, k, distances, ids, sel, with_id_map);
}
} // namespace faiss
#endif // COMPILE_SIMD_ARM_SVE
#endif // COMPILE_SIMD_ARM_NEON
@@ -5,11 +5,9 @@
* LICENSE file in the root directory of this source tree.
*/
#include <faiss/impl/fast_scan/pq4_fast_scan.h>
#include <faiss/impl/fast_scan/fast_scan.h>
#include <faiss/impl/FaissAssert.h>
#include <faiss/impl/fast_scan/LookupTableScaler.h>
#include <faiss/impl/fast_scan/kernels_simd256.h>
#include <faiss/impl/fast_scan/accumulate_loops.h>
#include <faiss/impl/fast_scan/simd_result_handlers.h>
namespace faiss {
@@ -22,63 +20,6 @@ using namespace simd_result_handlers;
namespace {
template <int NQ, int BB, class ResultHandler, class Scaler>
void accumulate_fixed_blocks(
size_t nb,
int nsq,
const uint8_t* codes,
const uint8_t* LUT,
ResultHandler& res,
const Scaler& scaler,
size_t block_stride) {
constexpr int bbs = 32 * BB;
for (size_t j0 = 0; j0 < nb; j0 += bbs) {
FixedStorageHandler<NQ, 2 * BB> res2;
kernel_accumulate_block<NQ, BB>(nsq, codes, LUT, res2, scaler);
res.set_block_origin(0, j0);
res2.to_other_handler(res);
codes += block_stride;
}
}
template <class ResultHandler, class Scaler>
void pq4_accumulate_loop_fixed_scaler(
int nq,
size_t nb,
int bbs,
int nsq,
const uint8_t* codes,
const uint8_t* LUT,
ResultHandler& res,
const Scaler& scaler,
size_t block_stride) {
FAISS_THROW_IF_NOT(is_aligned_pointer(codes));
FAISS_THROW_IF_NOT(is_aligned_pointer(LUT));
FAISS_THROW_IF_NOT(bbs % 32 == 0);
FAISS_THROW_IF_NOT(nb % bbs == 0);
#define DISPATCH(NQ, BB) \
case NQ * 1000 + BB: \
accumulate_fixed_blocks<NQ, BB>( \
nb, nsq, codes, LUT, res, scaler, block_stride); \
break
switch (nq * 1000 + bbs / 32) {
DISPATCH(1, 1);
DISPATCH(1, 2);
DISPATCH(1, 3);
DISPATCH(1, 4);
DISPATCH(1, 5);
DISPATCH(2, 1);
DISPATCH(2, 2);
DISPATCH(3, 1);
DISPATCH(4, 1);
default:
FAISS_THROW_FMT("nq=%d bbs=%d not instantiated", nq, bbs);
}
#undef DISPATCH
}
template <class ResultHandler>
void pq4_accumulate_loop_fixed_handler(
int nq,
@@ -5,7 +5,7 @@
* LICENSE file in the root directory of this source tree.
*/
#include <faiss/impl/fast_scan/pq4_fast_scan.h>
#include <faiss/impl/fast_scan/fast_scan.h>
#include <faiss/impl/FaissAssert.h>
#include <faiss/impl/fast_scan/LookupTableScaler.h>
+1 -1
View File
@@ -13,7 +13,7 @@
#include <faiss/IndexPQFastScan.h>
#include <faiss/impl/ProductQuantizer.h>
#include <faiss/impl/fast_scan/pq4_fast_scan.h>
#include <faiss/impl/fast_scan/fast_scan.h>
namespace {
+1 -1
View File
@@ -12,7 +12,7 @@
#include <vector>
#include <faiss/IndexIVF.h>
#include <faiss/impl/fast_scan/pq4_fast_scan.h>
#include <faiss/impl/fast_scan/fast_scan.h>
#include <faiss/utils/AlignedTable.h>
#include <faiss/utils/random.h>