Skip to content

Commit 57bf474

Browse files
alibeklfcmeta-codesync[bot]
authored andcommitted
Fix int/size_t signedness mismatches in HNSW add (#5116)
Summary: Pull Request resolved: #5116 The `hnsw_add_vertices()` function in both `IndexHNSW.cpp` and `IndexBinaryHNSW.cpp` accepts `size_t n0` and `size_t n` parameters, but the loop variables and counters inside were declared as `int`. This caused signed/unsigned comparison mismatches and implicit narrowing conversions. This diff fixes all such mismatches with six targeted changes: 1. **Loop variables `i0`, `i1`, `j` changed from `int` to `size_t`; OpenMP loop variable `i` changed from `int` to `int64_t`**: These variables derive from `n` (which is `size_t`) and are used as indices into `std::vector<int> order(n)`. Using `int` produced signed/unsigned comparison warnings in loop conditions (`j < i1`, `i < i1`) and risked undefined behavior via signed integer overflow for indices above `INT_MAX` (~2.1B). The outer variables use `size_t` to match the function parameter types. The OpenMP `for` loop variable uses `int64_t` because MSVC enforces that OpenMP loop counters must have signed integral types (`error C3016`), and `size_t` (unsigned) violates this requirement. `int64_t` is signed, 64-bit, and compatible with both the `size_t` loop bounds and MSVC OpenMP. 2. **`int n0 = ntotal` changed to `size_t n0 = ntotal` in `add()` methods**: `ntotal` is `idx_t` (`int64_t`), and `n0` is passed directly to `hnsw_add_vertices(..., size_t n0, ...)`. The `int` declaration caused an implicit 64-to-32-bit narrowing conversion at the call site. 3. **`printf` format specifiers changed from `%d` to `%zu`**: Required to match the new `size_t` types. Using `%d` with `size_t` arguments is undefined behavior per the C standard. 4. **`prev_display` sentinel logic refactored**: The old pattern used `int prev_display = verbose ? 0 : -1` with a `prev_display >= 0` guard, encoding a boolean in a signed integer sentinel. This was incompatible with the `size_t` migration (cannot represent -1). Replaced with an explicit `bool do_display` flag and `size_t prev_display = 0`, which is semantically identical and clearer. 5. **`std::make_unique<IndexBinaryFlat>(d_).release()` simplified to `new IndexBinaryFlat(d_)`**: The `make_unique().release()` pattern creates a `unique_ptr` only to immediately release ownership, which is equivalent to `new` but with unnecessary intermediate allocation tracking. Since `storage` is a raw pointer with `own_fields = true` ownership semantics (deleted in the destructor), plain `new` matches the established Faiss ownership pattern used throughout the codebase. 6. **`static_cast<int>` added at `rand_int()` call sites**: The `int`-to-`size_t` migration changed `i1 - j` from `int` to `size_t`, introducing a new `clang-diagnostic-shorten-64-to-32` warning when passed to `rand_int(int max)`. The explicit cast documents the intentional narrowing. The value is safe: `i1 - j` represents the remaining elements in a single HNSW level bucket, bounded well below `INT_MAX`. All changes are mechanical. No functional changes. Reviewed By: junjieqi Differential Revision: D101353511 fbshipit-source-id: a4a44b6a5436a77d97d47389db52b8721b2f97c4
1 parent 582246b commit 57bf474

2 files changed

Lines changed: 27 additions & 23 deletions

File tree

faiss/IndexBinaryHNSW.cpp

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -99,20 +99,22 @@ void hnsw_add_vertices(
9999
{ // perform add
100100
RandomGenerator rng2(789);
101101

102-
int i1 = n;
102+
size_t i1 = n;
103103

104104
for (int pt_level = static_cast<int>(hist.size()) - 1;
105105
pt_level >= int(!index_hnsw.init_level0);
106106
pt_level--) {
107-
int i0 = i1 - hist[pt_level];
107+
size_t i0 = i1 - hist[pt_level];
108108

109109
if (verbose) {
110-
printf("Adding %d elements at level %d\n", i1 - i0, pt_level);
110+
printf("Adding %zu elements at level %d\n", i1 - i0, pt_level);
111111
}
112112

113113
// random permutation to get rid of dataset order bias
114-
for (int j = i0; j < i1; j++) {
115-
std::swap(order[j], order[j + rng2.rand_int(i1 - j)]);
114+
for (size_t j = i0; j < i1; j++) {
115+
std::swap(
116+
order[j],
117+
order[j + rng2.rand_int(static_cast<int>(i1 - j))]);
116118
}
117119

118120
#pragma omp parallel
@@ -121,11 +123,11 @@ void hnsw_add_vertices(
121123

122124
std::unique_ptr<DistanceComputer> dis(
123125
index_hnsw.get_distance_computer());
124-
int prev_display =
125-
verbose && omp_get_thread_num() == 0 ? 0 : -1;
126+
bool do_display = verbose && omp_get_thread_num() == 0;
127+
size_t prev_display = 0;
126128

127129
#pragma omp for schedule(dynamic)
128-
for (int i = i0; i < i1; i++) {
130+
for (int64_t i = i0; i < i1; i++) {
129131
HNSW::storage_idx_t pt_id = order[i];
130132
dis->set_query(
131133
(float*)(x + (pt_id - n0) * index_hnsw.code_size));
@@ -138,9 +140,9 @@ void hnsw_add_vertices(
138140
vt,
139141
index_hnsw.keep_max_size_level0 && (pt_level == 0));
140142

141-
if (prev_display >= 0 && i - i0 > prev_display + 10000) {
143+
if (do_display && i - i0 > prev_display + 10000) {
142144
prev_display = i - i0;
143-
printf(" %d / %d\r", i - i0, i1 - i0);
145+
printf(" %zu / %zu\r", i - i0, i1 - i0);
144146
fflush(stdout);
145147
}
146148
}
@@ -176,7 +178,7 @@ IndexBinaryHNSW::IndexBinaryHNSW(int d_, int M)
176178
: IndexBinary(d_),
177179
hnsw(M),
178180
own_fields(true),
179-
storage(std::make_unique<IndexBinaryFlat>(d_).release()) {
181+
storage(new IndexBinaryFlat(d_)) {
180182
is_trained = true;
181183
}
182184

@@ -257,7 +259,7 @@ void IndexBinaryHNSW::search(
257259

258260
void IndexBinaryHNSW::add(idx_t n, const uint8_t* x) {
259261
FAISS_THROW_IF_NOT(is_trained);
260-
int n0 = ntotal;
262+
size_t n0 = ntotal;
261263
storage->add(n, x);
262264
ntotal = storage->ntotal;
263265

faiss/IndexHNSW.cpp

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -122,20 +122,22 @@ void hnsw_add_vertices(
122122
{ // perform add
123123
RandomGenerator rng2(789);
124124

125-
int i1 = n;
125+
size_t i1 = n;
126126

127127
for (int pt_level = static_cast<int>(hist.size()) - 1;
128128
pt_level >= int(!index_hnsw.init_level0);
129129
pt_level--) {
130-
int i0 = i1 - hist[pt_level];
130+
size_t i0 = i1 - hist[pt_level];
131131

132132
if (verbose) {
133-
printf("Adding %d elements at level %d\n", i1 - i0, pt_level);
133+
printf("Adding %zu elements at level %d\n", i1 - i0, pt_level);
134134
}
135135

136136
// random permutation to get rid of dataset order bias
137-
for (int j = i0; j < i1; j++) {
138-
std::swap(order[j], order[j + rng2.rand_int(i1 - j)]);
137+
for (size_t j = i0; j < i1; j++) {
138+
std::swap(
139+
order[j],
140+
order[j + rng2.rand_int(static_cast<int>(i1 - j))]);
139141
}
140142

141143
bool interrupt = false;
@@ -146,15 +148,15 @@ void hnsw_add_vertices(
146148

147149
std::unique_ptr<DistanceComputer> dis(
148150
storage_distance_computer(index_hnsw.storage));
149-
int prev_display =
150-
verbose && omp_get_thread_num() == 0 ? 0 : -1;
151+
bool do_display = verbose && omp_get_thread_num() == 0;
152+
size_t prev_display = 0;
151153
size_t counter = 0;
152154

153155
// here we should do schedule(dynamic) but this segfaults for
154156
// some versions of LLVM. The performance impact should not be
155157
// too large when (i1 - i0) / num_threads >> 1
156158
#pragma omp for schedule(static)
157-
for (int i = i0; i < i1; i++) {
159+
for (int64_t i = i0; i < i1; i++) {
158160
storage_idx_t pt_id = order[i];
159161
dis->set_query(x + (pt_id - n0) * d);
160162

@@ -171,9 +173,9 @@ void hnsw_add_vertices(
171173
vt,
172174
index_hnsw.keep_max_size_level0 && (pt_level == 0));
173175

174-
if (prev_display >= 0 && i - i0 > prev_display + 10000) {
176+
if (do_display && i - i0 > prev_display + 10000) {
175177
prev_display = i - i0;
176-
printf(" %d / %d\r", i - i0, i1 - i0);
178+
printf(" %zu / %zu\r", i - i0, i1 - i0);
177179
fflush(stdout);
178180
}
179181
if (counter % check_period == 0) {
@@ -349,7 +351,7 @@ void IndexHNSW::add(idx_t n, const float* x) {
349351
storage,
350352
"Please use IndexHNSWFlat (or variants) instead of IndexHNSW directly");
351353
FAISS_THROW_IF_NOT(is_trained);
352-
int n0 = ntotal;
354+
size_t n0 = ntotal;
353355
storage->add(n, x);
354356
ntotal = storage->ntotal;
355357

0 commit comments

Comments
 (0)