Skip to content

Commit 10e0795

Browse files
authored
Reduce binary size of refine functions (#1095)
The refine functions that work with GPU data use IVF-Flat under the hood to perform the refinement operation. This PR adds extern template declarations for `ivfflat_interleaved_scan` and uses these in the refine functions. This way we avoid recompiling the IVF-Flat search kernels, and save binary size. Before this PR `ivfflat_interleaved_scan` was compiled through the `ivf_flat::search()` function instantiations. But the function symbols were not available due to inlining. This PR also add explicit instantiations for `ivfflat_interleaved_scan`, and now both `ivf_flat::search` and `refine` can use the same interleaved scan function. Authors: - Tamas Bela Feher (https://github.com/tfeher) Approvers: - Artem M. Chirkin (https://github.com/achirkin) - Divye Gala (https://github.com/divyegala) URL: #1095
1 parent 1bc0fa5 commit 10e0795

16 files changed

Lines changed: 400 additions & 50 deletions

cpp/CMakeLists.txt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -432,6 +432,14 @@ if(BUILD_SHARED_LIBS)
432432
src/neighbors/ivf_flat/ivf_flat_search_half_int64_t.cu
433433
src/neighbors/ivf_flat/ivf_flat_search_int8_t_int64_t.cu
434434
src/neighbors/ivf_flat/ivf_flat_search_uint8_t_int64_t.cu
435+
src/neighbors/ivf_flat/ivf_flat_interleaved_scan_float_int64_t.cu
436+
src/neighbors/ivf_flat/ivf_flat_interleaved_scan_half_int64_t.cu
437+
src/neighbors/ivf_flat/ivf_flat_interleaved_scan_int8_t_int64_t.cu
438+
src/neighbors/ivf_flat/ivf_flat_interleaved_scan_uint8_t_int64_t.cu
439+
src/neighbors/ivf_flat/ivf_flat_interleaved_scan_float_int64_t_bitset.cu
440+
src/neighbors/ivf_flat/ivf_flat_interleaved_scan_half_int64_t_bitset.cu
441+
src/neighbors/ivf_flat/ivf_flat_interleaved_scan_int8_t_int64_t_bitset.cu
442+
src/neighbors/ivf_flat/ivf_flat_interleaved_scan_uint8_t_int64_t_bitset.cu
435443
src/neighbors/ivf_flat/ivf_flat_serialize_float_int64_t.cu
436444
src/neighbors/ivf_flat/ivf_flat_serialize_half_int64_t.cu
437445
src/neighbors/ivf_flat/ivf_flat_serialize_int8_t_int64_t.cu

cpp/src/neighbors/ivf_common.cuh

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2024, NVIDIA CORPORATION.
2+
* Copyright (c) 2024-2025, NVIDIA CORPORATION.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -148,17 +148,19 @@ __device__ inline auto find_chunk_ix(uint32_t& sample_ix, // NOLINT
148148
return ix_min;
149149
}
150150

151-
template <int BlockDim, typename IdxT>
151+
template <int BlockDim, typename IdxT, typename DbIdxT>
152152
__launch_bounds__(BlockDim) RAFT_KERNEL
153153
postprocess_neighbors_kernel(IdxT* neighbors_out, // [n_queries, topk]
154154
const uint32_t* neighbors_in, // [n_queries, topk]
155-
const IdxT* const* db_indices, // [n_clusters][..]
155+
const DbIdxT* const* db_indices, // [n_clusters][..]
156156
const uint32_t* clusters_to_probe, // [n_queries, n_probes]
157157
const uint32_t* chunk_indices, // [n_queries, n_probes]
158158
uint32_t n_queries,
159159
uint32_t n_probes,
160160
uint32_t topk)
161161
{
162+
static_assert(!raft::is_narrowing_v<uint32_t, IdxT>,
163+
"IdxT must be able to represent all values of uint32_t");
162164
const uint64_t i = threadIdx.x + BlockDim * uint64_t(blockIdx.x);
163165
const uint32_t query_ix = i / uint64_t(topk);
164166
if (query_ix >= n_queries) { return; }
@@ -170,8 +172,8 @@ __launch_bounds__(BlockDim) RAFT_KERNEL
170172
uint32_t data_ix = neighbors_in[k];
171173
const uint32_t chunk_ix = find_chunk_ix(data_ix, n_probes, chunk_indices);
172174
const bool valid = chunk_ix < n_probes;
173-
neighbors_out[k] =
174-
valid ? db_indices[clusters_to_probe[chunk_ix]][data_ix] : kOutOfBoundsRecord<IdxT>;
175+
neighbors_out[k] = valid ? static_cast<IdxT>(db_indices[clusters_to_probe[chunk_ix]][data_ix])
176+
: kOutOfBoundsRecord<IdxT>;
175177
}
176178

177179
/**
@@ -181,10 +183,10 @@ __launch_bounds__(BlockDim) RAFT_KERNEL
181183
* probed clusters / defined by the `chunk_indices`.
182184
* We assume the searched sample sizes (for a single query) fit into `uint32_t`.
183185
*/
184-
template <typename IdxT>
186+
template <typename IdxT, typename DbIdxT>
185187
void postprocess_neighbors(IdxT* neighbors_out, // [n_queries, topk]
186188
const uint32_t* neighbors_in, // [n_queries, topk]
187-
const IdxT* const* db_indices, // [n_clusters][..]
189+
const DbIdxT* const* db_indices, // [n_clusters][..]
188190
const uint32_t* clusters_to_probe, // [n_queries, n_probes]
189191
const uint32_t* chunk_indices, // [n_queries, n_probes]
190192
uint32_t n_queries,

cpp/src/neighbors/ivf_flat/ivf_flat_build.cuh

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2022-2024, NVIDIA CORPORATION.
2+
* Copyright (c) 2022-2025, NVIDIA CORPORATION.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -100,8 +100,9 @@ auto clone(const raft::resources& res, const index<T, IdxT>& source) -> index<T,
100100
* there are no dependencies between threads, hence no constraints on the block size.
101101
*
102102
* @tparam T element type.
103-
* @tparam IdxT type of the indices in the source source_vecs
103+
* @tparam IdxT type of the vector ids in the index (corresponds to second arg ofindex<T, IdxT>)
104104
* @tparam LabelT label type
105+
* @tparam SourceIndexT input index type (usually same as IdxT)
105106
* @tparam gather_src if false, then we build the index from vectors source_vecs[i,:], otherwise
106107
* we use source_vecs[source_ixs[i],:]. In both cases i=0..n_rows-1.
107108
*
@@ -118,10 +119,10 @@ auto clone(const raft::resources& res, const index<T, IdxT>& source) -> index<T,
118119
* @param veclen size of vectorized loads/stores; must satisfy `dim % veclen == 0`.
119120
*
120121
*/
121-
template <typename T, typename IdxT, typename LabelT, bool gather_src = false>
122+
template <typename T, typename IdxT, typename LabelT, typename SourceIdxT, bool gather_src = false>
122123
RAFT_KERNEL build_index_kernel(const LabelT* labels,
123124
const T* source_vecs,
124-
const IdxT* source_ixs,
125+
const SourceIdxT* source_ixs,
125126
T** list_data_ptrs,
126127
IdxT** list_index_ptrs,
127128
uint32_t* list_sizes_ptr,
@@ -135,7 +136,10 @@ RAFT_KERNEL build_index_kernel(const LabelT* labels,
135136
auto source_ix = source_ixs == nullptr ? i + batch_offset : source_ixs[i];
136137
// In the context of refinement, some indices may be invalid (the generating NN algorithm does
137138
// not return enough valid items). Do not add the item to the index in this case.
138-
if (source_ix == ivf::kInvalidRecord<IdxT> || source_ix == raft::upper_bound<IdxT>()) { return; }
139+
if (source_ix == ivf::kInvalidRecord<SourceIdxT> ||
140+
source_ix == raft::upper_bound<SourceIdxT>()) {
141+
return;
142+
}
139143

140144
auto list_id = labels[i];
141145
auto inlist_id = atomicAdd(list_sizes_ptr + list_id, 1);
@@ -460,11 +464,11 @@ inline auto build(raft::resources const& handle,
460464
* @param[in] candidate_idx device pointer to neighbor candidates, size [n_queries, n_candidates]
461465
* @param[in] n_candidates of neighbor_candidates
462466
*/
463-
template <typename T, typename IdxT>
467+
template <typename T, typename IdxT, typename CandidateIdxT>
464468
inline void fill_refinement_index(raft::resources const& handle,
465469
index<T, IdxT>* refinement_index,
466470
const T* dataset,
467-
const IdxT* candidate_idx,
471+
const CandidateIdxT* candidate_idx,
468472
IdxT n_queries,
469473
uint32_t n_candidates)
470474
{
@@ -500,7 +504,7 @@ inline void fill_refinement_index(raft::resources const& handle,
500504

501505
const dim3 block_dim(256);
502506
const dim3 grid_dim(raft::ceildiv<IdxT>(n_queries * n_candidates, block_dim.x));
503-
build_index_kernel<T, IdxT, LabelT, true>
507+
build_index_kernel<T, IdxT, LabelT, CandidateIdxT, true>
504508
<<<grid_dim, block_dim, 0, stream>>>(new_labels.data(),
505509
dataset,
506510
candidate_idx,

cpp/src/neighbors/ivf_flat/ivf_flat_interleaved_scan.cuh

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2022-2024, NVIDIA CORPORATION.
2+
* Copyright (c) 2022-2025, NVIDIA CORPORATION.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -40,11 +40,6 @@ using namespace cuvs::spatial::knn::detail; // NOLINT
4040

4141
constexpr int kThreadsPerBlock = 128;
4242

43-
auto RAFT_WEAK_FUNCTION is_local_topk_feasible(uint32_t k) -> bool
44-
{
45-
return k <= raft::matrix::detail::select::warpsort::kMaxCapacity;
46-
}
47-
4843
/**
4944
* @brief Copy `n` elements per block from one place to another.
5045
*
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
/*
2+
* Copyright (c) 2025, NVIDIA CORPORATION.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
#pragma once
18+
19+
#include "../detail/ann_utils.cuh"
20+
#include "ivf_flat_interleaved_scan.cuh"
21+
#include <cstdint>
22+
#include <cuvs/neighbors/common.hpp>
23+
#include <cuvs/neighbors/ivf_flat.hpp>
24+
#include <raft/core/resource/cuda_stream.hpp>
25+
#include <raft/core/resources.hpp>
26+
27+
#define CUVS_INST_IVF_FLAT_INTERLEAVED_SCAN(T, IdxT, SampleFilterT) \
28+
template void \
29+
ivfflat_interleaved_scan<T, \
30+
typename cuvs::spatial::knn::detail::utils::config<T>::value_t, \
31+
IdxT, \
32+
SampleFilterT>(const index<T, IdxT>& index, \
33+
const T* queries, \
34+
const uint32_t* coarse_query_results, \
35+
const uint32_t n_queries, \
36+
const uint32_t queries_offset, \
37+
const cuvs::distance::DistanceType metric, \
38+
const uint32_t n_probes, \
39+
const uint32_t k, \
40+
const uint32_t max_samples, \
41+
const uint32_t* chunk_indices, \
42+
const bool select_min, \
43+
SampleFilterT sample_filter, \
44+
uint32_t* neighbors, \
45+
float* distances, \
46+
uint32_t& grid_dim_x, \
47+
rmm::cuda_stream_view stream);
48+
49+
#define COMMA ,
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
/*
2+
* Copyright (c) 2025, NVIDIA CORPORATION.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
#pragma once
18+
19+
#include <cstdint>
20+
#include <cuda_fp16.h>
21+
22+
#include "../detail/ann_utils.cuh"
23+
#include <cuvs/neighbors/common.hpp>
24+
#include <raft/core/resource/cuda_stream.hpp>
25+
#include <raft/core/resources.hpp>
26+
#include <raft/util/raft_explicit.hpp>
27+
28+
namespace cuvs::neighbors::ivf_flat::detail {
29+
template <typename T, typename AccT, typename IdxT, typename IvfSampleFilterT>
30+
void ivfflat_interleaved_scan(const index<T, IdxT>& index,
31+
const T* queries,
32+
const uint32_t* coarse_query_results,
33+
const uint32_t n_queries,
34+
const uint32_t queries_offset,
35+
const cuvs::distance::DistanceType metric,
36+
const uint32_t n_probes,
37+
const uint32_t k,
38+
const uint32_t max_samples,
39+
const uint32_t* chunk_indices,
40+
const bool select_min,
41+
IvfSampleFilterT sample_filter,
42+
uint32_t* neighbors,
43+
float* distances,
44+
uint32_t& grid_dim_x,
45+
rmm::cuda_stream_view stream) RAFT_EXPLICIT;
46+
47+
#define CUVS_INST_IVF_FLAT_INTERLEAVED_SCAN(T, IdxT, SampleFilterT) \
48+
extern template void \
49+
ivfflat_interleaved_scan<T, \
50+
typename cuvs::spatial::knn::detail::utils::config<T>::value_t, \
51+
IdxT, \
52+
SampleFilterT>(const index<T, IdxT>& index, \
53+
const T* queries, \
54+
const uint32_t* coarse_query_results, \
55+
const uint32_t n_queries, \
56+
const uint32_t queries_offset, \
57+
const cuvs::distance::DistanceType metric, \
58+
const uint32_t n_probes, \
59+
const uint32_t k, \
60+
const uint32_t max_samples, \
61+
const uint32_t* chunk_indices, \
62+
const bool select_min, \
63+
SampleFilterT sample_filter, \
64+
uint32_t* neighbors, \
65+
float* distances, \
66+
uint32_t& grid_dim_x, \
67+
rmm::cuda_stream_view stream);
68+
69+
CUVS_INST_IVF_FLAT_INTERLEAVED_SCAN(float, int64_t, cuvs::neighbors::filtering::none_sample_filter);
70+
71+
CUVS_INST_IVF_FLAT_INTERLEAVED_SCAN(half, int64_t, cuvs::neighbors::filtering::none_sample_filter);
72+
73+
CUVS_INST_IVF_FLAT_INTERLEAVED_SCAN(int8_t,
74+
int64_t,
75+
cuvs::neighbors::filtering::none_sample_filter);
76+
77+
CUVS_INST_IVF_FLAT_INTERLEAVED_SCAN(uint8_t,
78+
int64_t,
79+
cuvs::neighbors::filtering::none_sample_filter);
80+
81+
#define COMMA ,
82+
CUVS_INST_IVF_FLAT_INTERLEAVED_SCAN(
83+
float, int64_t, cuvs::neighbors::filtering::bitset_filter<uint32_t COMMA int64_t>);
84+
85+
CUVS_INST_IVF_FLAT_INTERLEAVED_SCAN(
86+
half, int64_t, cuvs::neighbors::filtering::bitset_filter<uint32_t COMMA int64_t>);
87+
88+
CUVS_INST_IVF_FLAT_INTERLEAVED_SCAN(
89+
int8_t, int64_t, cuvs::neighbors::filtering::bitset_filter<uint32_t COMMA int64_t>);
90+
91+
CUVS_INST_IVF_FLAT_INTERLEAVED_SCAN(
92+
uint8_t, int64_t, cuvs::neighbors::filtering::bitset_filter<uint32_t COMMA int64_t>);
93+
#undef COMMA
94+
#undef CUVS_INST_IVF_FLAT_INTERLEAVED_SCAN
95+
96+
} // namespace cuvs::neighbors::ivf_flat::detail
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
/*
2+
* Copyright (c) 2025, NVIDIA CORPORATION.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
#include "ivf_flat_interleaved_scan_explicit_inst.cuh"
17+
18+
namespace cuvs::neighbors::ivf_flat::detail {
19+
20+
CUVS_INST_IVF_FLAT_INTERLEAVED_SCAN(float, int64_t, filtering::none_sample_filter);
21+
} // namespace cuvs::neighbors::ivf_flat::detail
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
/*
2+
* Copyright (c) 2025, NVIDIA CORPORATION.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
#include "ivf_flat_interleaved_scan_explicit_inst.cuh"
17+
18+
namespace cuvs::neighbors::ivf_flat::detail {
19+
20+
CUVS_INST_IVF_FLAT_INTERLEAVED_SCAN(float,
21+
int64_t,
22+
filtering::bitset_filter<uint32_t COMMA int64_t>);
23+
} // namespace cuvs::neighbors::ivf_flat::detail
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/*
2+
* Copyright (c) 2025, NVIDIA CORPORATION.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
#include "ivf_flat_interleaved_scan_explicit_inst.cuh"
18+
#include <cuda_fp16.h>
19+
20+
namespace cuvs::neighbors::ivf_flat::detail {
21+
22+
CUVS_INST_IVF_FLAT_INTERLEAVED_SCAN(half, int64_t, filtering::none_sample_filter);
23+
24+
} // namespace cuvs::neighbors::ivf_flat::detail
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/*
2+
* Copyright (c) 2025, NVIDIA CORPORATION.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
#include "ivf_flat_interleaved_scan_explicit_inst.cuh"
18+
#include <cuda_fp16.h>
19+
20+
namespace cuvs::neighbors::ivf_flat::detail {
21+
22+
CUVS_INST_IVF_FLAT_INTERLEAVED_SCAN(half,
23+
int64_t,
24+
filtering::bitset_filter<uint32_t COMMA int64_t>);
25+
26+
} // namespace cuvs::neighbors::ivf_flat::detail

0 commit comments

Comments
 (0)