Remove RaBitQStats debug counters from all RaBitQ handlers (#5102)

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

### THIS DIFF
Remove the `RaBitQStats` struct, global variable, and all `#ifndef NDEBUG` stat-tracking blocks from the four RaBitQ index implementations:
- `IndexRaBitQ` (non-FastScan flat)
- `IndexIVFRaBitQ` (non-FastScan IVF)
- `RaBitQHeapHandler` (FastScan flat, in IndexRaBitQFastScan.h)
- `IVFRaBitQHeapHandler` (FastScan IVF, in IndexIVFRaBitQFastScan.h)

The stats (`n_1bit_evaluations`, `n_multibit_evaluations`) were gated behind `#ifndef NDEBUG` and only available in dev builds. They tracked how many candidates passed the 1-bit lower-bound filter vs required full multibit refinement. While useful during initial development, they add per-vector overhead in dev builds (counter increments + two `omp atomic` updates per handle() call) and are no longer needed for ongoing work.

All references are removed: the header, source file, SWIG bindings, xplat.bzl, and CMakeLists.txt registrations.

### CONTEXT
Profiling of `IVFRaBitQHeapHandler::handle()` showed that debug stat tracking contributes measurable overhead in dev builds. Removing them simplifies the handler code and eliminates a source of noise in performance measurements. The filtering effectiveness can be measured externally via search result analysis when needed.

Reviewed By: ALittleYam

Differential Revision: D100931175

fbshipit-source-id: 7d148b63c5fca1b5f0e09040db2e3d77ffad7cbb
This commit is contained in:
Alibek Zhakubayev
2026-04-15 08:42:26 -07:00
committed by meta-codesync[bot]
parent 796977a1ab
commit 74dceee903
11 changed files with 1 additions and 195 deletions
-2
View File
@@ -120,7 +120,6 @@ set(FAISS_SRC
impl/AdditiveQuantizer.cpp
impl/RaBitQuantizer.cpp
impl/RaBitQuantizerMultiBit.cpp
impl/RaBitQStats.cpp
impl/RaBitQUtils.cpp
impl/ResidualQuantizer.cpp
impl/LocalSearchQuantizer.cpp
@@ -254,7 +253,6 @@ set(FAISS_HEADERS
impl/Quantizer.h
impl/RaBitQuantizer.h
impl/RaBitQuantizerMultiBit.h
impl/RaBitQStats.h
impl/RaBitQUtils.h
impl/ResidualQuantizer.h
impl/ResultHandler.h
-30
View File
@@ -231,14 +231,6 @@ struct RaBitInvertedListScanner : InvertedListScanner {
// Multi-bit: Two-stage search with adaptive filtering
size_t nup = 0;
// Stats tracking for multi-bit two-stage search
// n_1bit_evaluations: candidates evaluated using 1-bit lower bound
// n_multibit_evaluations: candidates requiring full multi-bit distance
#ifndef NDEBUG
size_t local_1bit_evaluations = 0;
size_t local_multibit_evaluations = 0;
#endif
for (size_t j = 0; j < list_size; j++) {
if (sel != nullptr) {
int64_t id = store_pairs ? lo_build(list_no, j) : ids[j];
@@ -248,18 +240,8 @@ struct RaBitInvertedListScanner : InvertedListScanner {
}
}
#ifndef NDEBUG
local_1bit_evaluations++;
#endif
// Stage 1: Compute distance bound using 1-bit codes
// For L2 (min-heap): use lower_bound to safely skip if it's
// already worse than heap worst
// For IP (max-heap): use upper_bound because with a lower bound,
// we can't safely skip any candidate
float est_distance = rabitq_dc->distance_to_code_1bit(codes);
// Extract f_error and g_error for filtering
size_t code_size_base = (ivf_rabitq.d + 7) / 8;
const rabitq_utils::SignBitFactorsWithError* base_fac =
reinterpret_cast<
@@ -273,10 +255,6 @@ struct RaBitInvertedListScanner : InvertedListScanner {
handler.threshold,
keep_max);
if (should_refine) {
#ifndef NDEBUG
local_multibit_evaluations++;
#endif
// Lower bound is promising, compute full distance
float dis = distance_to_code(codes);
int64_t id = store_pairs ? lo_build(list_no, j) : ids[j];
@@ -287,14 +265,6 @@ struct RaBitInvertedListScanner : InvertedListScanner {
codes += code_size;
}
#ifndef NDEBUG
// Update global stats atomically
#pragma omp atomic
rabitq_stats.n_1bit_evaluations += local_1bit_evaluations;
#pragma omp atomic
rabitq_stats.n_multibit_evaluations += local_multibit_evaluations;
#endif
return nup;
}
-1
View File
@@ -13,7 +13,6 @@
#include <faiss/Index.h>
#include <faiss/IndexIVF.h>
#include <faiss/impl/RaBitQStats.h>
#include <faiss/impl/RaBitQuantizer.h>
namespace faiss {
-19
View File
@@ -13,7 +13,6 @@
#include <faiss/IndexIVFFastScan.h>
#include <faiss/IndexIVFRaBitQ.h>
#include <faiss/IndexRaBitQFastScan.h>
#include <faiss/impl/RaBitQStats.h>
#include <faiss/impl/RaBitQUtils.h>
#include <faiss/impl/RaBitQuantizer.h>
#include <faiss/impl/fast_scan/rabitq_result_handler.h>
@@ -271,11 +270,6 @@ void IVFRaBitQHeapHandler<C, SL>::handle(
const size_t qb = context->qb > 0 ? context->qb : index->qb;
const size_t d = index->d;
#ifndef NDEBUG
size_t local_1bit_evaluations = 0;
size_t local_multibit_evaluations = 0;
#endif
for (size_t j = 0; j < max_positions; j++) {
const int64_t result_id = this->adjust_id(b, j);
if (result_id < 0) {
@@ -291,9 +285,6 @@ void IVFRaBitQHeapHandler<C, SL>::handle(
const uint8_t* base_ptr = aux_base + j * storage_size;
if (is_multibit) {
#ifndef NDEBUG
local_1bit_evaluations++;
#endif
const SignBitFactorsWithError& full_factors =
*reinterpret_cast<const SignBitFactorsWithError*>(base_ptr);
@@ -312,9 +303,6 @@ void IVFRaBitQHeapHandler<C, SL>::handle(
heap_dis[0],
is_similarity);
if (should_refine) {
#ifndef NDEBUG
local_multibit_evaluations++;
#endif
size_t local_offset = idx_base + j;
float dist_full = compute_full_multibit_distance(
local_q, q, local_offset, base_ptr);
@@ -342,13 +330,6 @@ void IVFRaBitQHeapHandler<C, SL>::handle(
}
}
}
#ifndef NDEBUG
#pragma omp atomic
rabitq_stats.n_1bit_evaluations += local_1bit_evaluations;
#pragma omp atomic
rabitq_stats.n_multibit_evaluations += local_multibit_evaluations;
#endif
}
template <class C, SIMDLevel SL>
+1 -34
View File
@@ -106,17 +106,8 @@ struct Run_search_with_dc_res {
resi.begin(q);
dc_base->set_query(xq + d * q);
// Stats tracking for multi-bit two-stage search only
// n_1bit_evaluations: candidates evaluated using 1-bit lower
// bound n_multibit_evaluations: candidates requiring full
// multi-bit distance
#ifndef NDEBUG
size_t local_1bit_evaluations = 0;
size_t local_multibit_evaluations = 0;
#endif
if (ex_bits == 0) {
// 1-bit: Standard single-stage search (no stats tracking)
// 1-bit: Standard single-stage search
for (size_t i = 0; i < ntotal; i++) {
if (res.is_in_selection(i)) {
float dis = (*dc_base)(i);
@@ -135,7 +126,6 @@ struct Run_search_with_dc_res {
dc != nullptr,
"Failed to cast to RaBitQDistanceComputer for two-stage search");
// Use appropriate comparison based on metric type
bool is_similarity =
is_similarity_metric(index->metric_type);
@@ -144,18 +134,9 @@ struct Run_search_with_dc_res {
const uint8_t* code =
index->codes.data() + i * index->code_size;
#ifndef NDEBUG
local_1bit_evaluations++;
#endif
// Stage 1: Compute distance bound using 1-bit codes
// For L2 (min-heap): use lower_bound (est -
// error) For IP (max-heap): use upper_bound (est
// + error)
float est_distance =
dc->distance_to_code_1bit(code);
// Extract f_error for filtering
size_t code_size_base = (index->d + 7) / 8;
const rabitq_utils::SignBitFactorsWithError*
base_fac = reinterpret_cast<
@@ -163,7 +144,6 @@ struct Run_search_with_dc_res {
SignBitFactorsWithError*>(
code + code_size_base);
// Stage 2: Adaptive filtering
bool should_refine =
rabitq_utils::should_refine_candidate(
est_distance,
@@ -172,10 +152,6 @@ struct Run_search_with_dc_res {
resi.threshold,
is_similarity);
if (should_refine) {
#ifndef NDEBUG
local_multibit_evaluations++;
#endif
// Compute full multi-bit distance
float dist_full =
dc->distance_to_code_full(code);
resi.add_result(dist_full, i);
@@ -184,15 +160,6 @@ struct Run_search_with_dc_res {
}
}
#ifndef NDEBUG
// Update global stats atomically
#pragma omp atomic
rabitq_stats.n_1bit_evaluations += local_1bit_evaluations;
#pragma omp atomic
rabitq_stats.n_multibit_evaluations +=
local_multibit_evaluations;
#endif
resi.end();
}
}
-1
View File
@@ -8,7 +8,6 @@
#pragma once
#include <faiss/IndexFlatCodes.h>
#include <faiss/impl/RaBitQStats.h>
#include <faiss/impl/RaBitQuantizer.h>
namespace faiss {
-20
View File
@@ -12,7 +12,6 @@
#include <faiss/IndexFastScan.h>
#include <faiss/IndexRaBitQ.h>
#include <faiss/impl/RaBitQStats.h>
#include <faiss/impl/RaBitQUtils.h>
#include <faiss/impl/RaBitQuantizer.h>
#include <faiss/impl/fast_scan/simd_result_handlers.h>
@@ -213,21 +212,12 @@ struct RaBitQHeapHandler
const uint8_t* aux_base = rabitq_index->codes.get() +
block_idx * full_block_size + packed_block_size;
#ifndef NDEBUG
size_t local_1bit_evaluations = 0;
size_t local_multibit_evaluations = 0;
#endif
for (size_t i = 0; i < max_vectors; i++) {
const size_t db_idx = base_db_idx + i;
const float normalized_distance = d32tab[i] * one_a + bias;
const uint8_t* base_ptr = aux_base + i * storage_size;
if (is_multi_bit) {
#ifndef NDEBUG
local_1bit_evaluations++;
#endif
const SignBitFactorsWithError& full_factors =
*reinterpret_cast<const SignBitFactorsWithError*>(
base_ptr);
@@ -252,9 +242,6 @@ struct RaBitQHeapHandler
is_similarity);
if (should_refine) {
#ifndef NDEBUG
local_multibit_evaluations++;
#endif
float dist_full = compute_full_multibit_distance(db_idx, q);
if (Cfloat::cmp(heap_dis[0], dist_full)) {
@@ -282,13 +269,6 @@ struct RaBitQHeapHandler
}
}
}
#ifndef NDEBUG
#pragma omp atomic
rabitq_stats.n_1bit_evaluations += local_1bit_evaluations;
#pragma omp atomic
rabitq_stats.n_multibit_evaluations += local_multibit_evaluations;
#endif
}
void begin(const float* norms) override {
-29
View File
@@ -1,29 +0,0 @@
/*
* 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.
*/
#include <faiss/impl/RaBitQStats.h>
namespace faiss {
// NOLINTNEXTLINE(facebook-avoid-non-const-global-variables)
RaBitQStats rabitq_stats;
void RaBitQStats::reset() {
n_1bit_evaluations = 0;
n_multibit_evaluations = 0;
}
double RaBitQStats::skip_percentage() const {
const size_t copy_n_1bit_evaluations = n_1bit_evaluations;
const size_t copy_n_multibit_evaluations = n_multibit_evaluations;
return copy_n_1bit_evaluations > 0
? 100.0 * (copy_n_1bit_evaluations - copy_n_multibit_evaluations) /
copy_n_1bit_evaluations
: 0.0;
}
} // namespace faiss
-56
View File
@@ -1,56 +0,0 @@
/*
* 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 <faiss/impl/platform_macros.h>
#include <cstddef>
namespace faiss {
/// Statistics for RaBitQ multi-bit two-stage search.
///
/// These stats are ONLY collected for multi-bit mode (nb_bits > 1).
/// In 1-bit mode, there is no two-stage filtering - all candidates are
/// evaluated with a single distance computation, so there is nothing
/// meaningful to track. For 1-bit mode, both counters remain 0.
///
/// Multi-bit mode uses a two-stage search:
/// Stage 1: Compute 1-bit lower bound distance for all candidates
/// Stage 2: Compute full multi-bit distance only for promising candidates
///
/// The skip_percentage() metric measures filtering effectiveness:
/// how many candidates were filtered out by the 1-bit lower bound
/// without needing the more expensive multi-bit distance computation.
///
/// WARNING: Statistics are not robust to internal threading nor to
/// concurrent RaBitQ searches. Use these values in a single-threaded
/// context to accurately gauge RaBitQ's filtering effectiveness.
/// Call reset() before search, then read stats after search completes.
struct RaBitQStats {
/// Number of candidates evaluated using 1-bit (lower bound) distance.
/// This is the first stage of two-stage search in multi-bit mode.
/// Always 0 in 1-bit mode (stats not tracked).
size_t n_1bit_evaluations = 0;
/// Number of candidates that passed 1-bit filtering and required
/// full multi-bit distance computation (second stage).
/// Always 0 in 1-bit mode (stats not tracked).
size_t n_multibit_evaluations = 0;
void reset();
/// Compute percentage of candidates skipped (filtered out by 1-bit stage).
/// Returns 0 if no candidates were evaluated (including 1-bit mode).
double skip_percentage() const;
};
/// Global stats for RaBitQ indexes
// NOLINTNEXTLINE(facebook-avoid-non-const-global-variables)
FAISS_API extern RaBitQStats rabitq_stats;
} // namespace faiss
@@ -11,7 +11,6 @@
#include <vector>
#include <faiss/impl/FaissAssert.h>
#include <faiss/impl/RaBitQStats.h>
#include <faiss/impl/RaBitQUtils.h>
#include <faiss/impl/fast_scan/FastScanDistancePostProcessing.h>
#include <faiss/impl/fast_scan/simd_result_handlers.h>
-2
View File
@@ -203,7 +203,6 @@ typedef uint64_t size_t;
#include <faiss/IndexNeuralNetCodec.h>
#include <faiss/impl/RaBitQuantizer.h>
#include <faiss/impl/RaBitQStats.h>
#include <faiss/impl/RaBitQUtils.h>
#include <faiss/IndexRaBitQ.h>
#include <faiss/IndexRaBitQFastScan.h>
@@ -668,7 +667,6 @@ void gpu_sync_all_devices()
%include <faiss/IndexNeuralNetCodec.h>
%include <faiss/impl/RaBitQuantizer.h>
%include <faiss/impl/RaBitQStats.h>
%include <faiss/impl/RaBitQUtils.h>
%include <faiss/IndexRaBitQ.h>
%include <faiss/IndexRaBitQFastScan.h>