Iterative CAGRA-Q - #1810
Conversation
|
Auto-sync is disabled for draft pull requests in this repository. Workflows must be run manually. Contributors can view more details about this message here. |
… search - Configurable growth-phase in-build search params (itopk_size, search_width, max_iterations) and internal/smem dtype; itopk auto-forced on the final full-size iteration. - Decouple compression params used during iterative construction from the target index compression. - Add shuffle_dataset option; fix out-of-bounds access from the in-place raft gather by switching to an out-of-place gather.
…around) The shuffle_dataset path used an out-of-place gather into a temporary buffer to work around an illegal memory access in raft's in-place gather overload when n_rows * row_len exceeded 2^31 (32-bit index overflow). That bug is now fixed upstream in raft (NVIDIA/raft#3059, closes #3055), which the cuvs raft pin now includes. Revert to the in-place gather to drop the extra full-size temporary allocation and copy.
8fc10ac to
4f4068a
Compare
…set on disk. Also added tests
…t to the algorithm as a path
…_dataset() is always true
…ches without the dense rows
achirkin
left a comment
There was a problem hiding this comment.
Please undo all changes to bench/ann/src/common. Let's keep CAGRA-Q compressed dataset support as before - by letting the compressed dataset be a part of index file / folder.
We should leave a proper refactoring of the benchmark harness for discussion and implementation in follow up work (if and how we want to pass compressed datasets as first-class input to the benchmarks).
I created a standalone PR for the vpq dataset serialisation. We can look through it and merge faster that this PR, and start working on the cuvsbench refactoring sooner. I need the refactoring to rerun the benchmarks (the older performance numbers are obsolete now). I won't be comfortable merging this PR into main before the new benchmarks land |
… graphs are already on the device
…ing the CAGRA-Q path
tarang-jain
left a comment
There was a problem hiding this comment.
Thanks for the great work!
| auto batch_queries = | ||
| vpq_queries != nullptr || dev_query_view.extent(1) != query_dim | ||
| ? raft::make_device_matrix<T, int64_t>(res, static_cast<int64_t>(max_chunk_size), query_dim) | ||
| : raft::make_device_matrix<T, int64_t>(res, 0, 0); |
There was a problem hiding this comment.
Looks like CAGRA search will pad these queries to the aligned dim if they are not aligned right? So why not make the batch itself padded here -- will avoid an extra D2D copy during cagra search (it might need the reconstruct kernel to jump the padded dim).
|
What do you think about putting the ANN bench changes in a separate PR (even though you want to benchmark this e2e)? That can be a follow up to this PR. |
tfeher
left a comment
There was a problem hiding this comment.
Thanks @irina-resh-nvda and @aamijar for this PR! My main questions below are about naming of params / APIs related to PQ compressed input. Otherwise, the PR looks great overall.
| * @brief Compress a dense dataset into a device VPQ dataset. | ||
| */ | ||
| CUVS_EXPORT cuvsError_t cuvsDatasetMakeVpq(cuvsResources_t res, | ||
| cuvsCagraCompressionParams_t params, |
There was a problem hiding this comment.
This function compresses the dataset into VPQ format. The user can decide later to use that for CAGRA, or for other algorithms. It would make sense to rename the params struct to something like cuvsPqParams.
| if (build_search_conf.contains("persistent")) { | ||
| arg.persistent = build_search_conf.at("persistent"); | ||
| } | ||
| if (build_search_conf.contains("persistent_lifetime")) { | ||
| arg.persistent_lifetime = build_search_conf.at("persistent_lifetime"); | ||
| } | ||
| if (build_search_conf.contains("persistent_device_usage")) { | ||
| arg.persistent_device_usage = build_search_conf.at("persistent_device_usage"); |
There was a problem hiding this comment.
I assume build_search is set of parameters used only during build. Is persistent mode used during build? I don't expect that, since with large batch search operations we have large throughput without the persistent kernel. My concern in exposing this as tunable build parameter is that it might motivate users to add these options to their build parameter scan, which is not productive.
If the goal is completeness in parameter handling, then shouldn't we use the same function that parses search params?
| if (out_dataset == nullptr) { | ||
| cuvs::neighbors::detail::skip_dense_dataset<T, int64_t>(res, is); | ||
| // Dropping the rows leaves a searchable index for a dense view, whose dataset can be | ||
| // reattached from the caller's own copy, but not for a VPQ one: the compressed rows exist |
There was a problem hiding this comment.
Why do we handle the pq compressed dataset differently? Can't the user reattach a pq compressed dataset to an index after the graph was loaded from file?
| final_graph_size = static_cast<uint64_t>(dataset.n_rows()); | ||
| vpq_dataset = &dataset.dset(); | ||
| } else { | ||
| auto search_dataset = ensure_device_padded_for_iterative_search<T>(res, dataset, padded_own); |
There was a problem hiding this comment.
I believe that using the new DataSet API, the guideline is that we do not make such copies for the user, but expect them to provide already padded data.
There was a problem hiding this comment.
so we basically error out if its not padded, right?
| logical_dim); | ||
| auto idx = cuvs::neighbors::cagra::device_padded_index<T, IdxT>(res, params.metric); | ||
| idx.update_device_dataset_same_layout(res, sub_padded); | ||
| if (use_device_graph) { |
There was a problem hiding this comment.
Expect of the first iteration, we always use dev_graph, right? Could you simply copy the initial cagra_graph to the device so that we can simplify the code here?
Keep VPQ serialize/deserialize and iterative-build search_main usage, and adopt KvikIO dataset I/O plus cuda_rt_essentials from the release branch.
| index_type idx(res, effective_params.metric); | ||
| idx.update_graph(res, raft::make_const_mdspan(cagra_graph.view())); | ||
| if (effective_params.attach_dataset_on_build) { | ||
| idx.update_device_dataset_same_layout(res, dataset); |
There was a problem hiding this comment.
I thought we got rid of this function update_device_dataset_same_layout, probably a merge conflict artifact
Those build_search_* knobs are preserved on iterative-cagra-q-ann-bench.
Build CAGRA on PQ datasets with Iterative CAGRA-Q
Iterative cagra graph construction using CAGRA-Q search.
This PR improves the iterative CAGRA build method by enabling PQ compression: the dataset is compressed before the iterative search starts, and CAGRA-Q is used to iteratively update the KNN graph.
This is the first time we are introducing building CAGRA on (PQ) quantized datasets directly.