Skip to content

Fast CAGRA Index Merge - #2352

Open
landrumb wants to merge 39 commits into
NVIDIA:mainfrom
landrumb:landrumb/cagra-fastener-merge
Open

Fast CAGRA Index Merge#2352
landrumb wants to merge 39 commits into
NVIDIA:mainfrom
landrumb:landrumb/cagra-fastener-merge

Conversation

@landrumb

@landrumb landrumb commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

This PR implements the Fastener graph merge operation. This PR supports merging for float, half, int8, and uint8 dtypes, and euclidean distances.

There was originally specialization to use int8 GEMM for the integer types, but I ran into portability issues on Ada and it turns out that using the same f32 path for both is simpler and not substantially slower. Currently investigating switching the unified path to TF32 to use tensor cores.

The core logic resides in cagra_merge_scaffold.cuh.

This PR adds 7.64 MiB to libcuvs.so, a 2.94% increase.

image (H100) image image image image image

@copy-pr-bot

copy-pr-bot Bot commented Jul 22, 2026

Copy link
Copy Markdown

This pull request requires additional validation before any workflows can run on NVIDIA's runners.

Pull request vetters can view their responsibilities here.

Contributors can view more details about this message here.

@copy-pr-bot

copy-pr-bot Bot commented Jul 22, 2026

Copy link
Copy Markdown

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.

@dantegd

dantegd commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

/ok to test 2c54392

@aamijar aamijar added non-breaking Introduces a non-breaking change feature request New feature or request labels Jul 22, 2026
@aamijar aamijar moved this to In Progress in Unstructured Data Processing Jul 22, 2026
@landrumb

Copy link
Copy Markdown
Contributor Author

/ok to test

@landrumb

Copy link
Copy Markdown
Contributor Author

/ok to test

@landrumb

Copy link
Copy Markdown
Contributor Author

/ok to test

@landrumb

Copy link
Copy Markdown
Contributor Author

/ok to test

@landrumb
landrumb marked this pull request as ready for review July 30, 2026 15:14
@landrumb
landrumb requested review from a team as code owners July 30, 2026 15:14
@landrumb landrumb changed the title [WIP] Fast CAGRA Index Merge Fast CAGRA Index Merge Jul 30, 2026
landrumb and others added 6 commits July 30, 2026 15:39
Brings the branch up to date with main (f72199e), which resolves the
check-c-abi failure: the checker was reporting cuvsResourcesSetWorkspacePool,
cuvsRMMAsyncMemoryResourceEnable, cuvsCagraSearchMultiPartition and
cuvsSelectK as removed functions, when in fact they were added upstream
after this branch's base and were simply missing here.

Only cpp/src/neighbors/cagra.cuh conflicted, and only positionally: both
sides append a new function immediately after the existing merge() overload.
Kept both -- this branch's merge() taking merge_params, and main's
multi-partition search() overload.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@landrumb

Copy link
Copy Markdown
Contributor Author

Currently failing because the wheel builds are too big. Not sure there's much I can do about this.

@achirkin achirkin left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for this extensive contribution! Since the datataset API changes are in, the main request is to rethink where we allocate the new, consolidated dataset during merge (see below).
Then, please try to refactor the code to use raft mdarrays and mdspans in place of RMM and use the workspace/large_workspace resources for all temporary allocations - this will help us to keep track of memory requirements (which seem to be plenty).

Comment thread cpp/src/neighbors/detail/cagra/cagra_merge_scaffold.cuh Outdated
Comment on lines +91 to +103
/** Device state and tuning knobs shared by every split level: the precomputed row norms, the
* tiling and workspace capacities, and the seed feeding the deterministic leader samples. */
struct split_context {
split_context(rmm::cuda_stream_view stream, int64_t rows)
: norms(static_cast<size_t>(rows), stream)
{
}

rmm::device_uvector<float> norms;
int assignment_tile_rows = ASSIGNMENT_TILE_ROWS;
size_t gemm_workspace_bytes = GEMM_WORKSPACE_BYTES;
uint64_t seed = DETERMINISTIC_SEED;
};

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's try to switch to raft primitives here: raft::device_vector<float> as the buffer and raft::resources as an argument.
Then, we need to consider: does this context fit into the bounded workspace memory or does it grow with the problem size unbounded? Depending on the answer, use raft::get_workspace_resource_ref or raft::get_large_workspace_resource_ref.
By doing this, we let the user control where the working memory for the algorithm resides, enable better accounting (via tools like raft::memory_tracking_resources), and prepare for the dry run execution (feature soon to be enabled in raft).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've gone through and I think this is done correctly now, let me know if I misunderstand the usage of the raft allocs. Is there a way I should be testing preparedness for dry run execution?

Comment thread cpp/src/neighbors/detail/cagra/cagra_merge_scaffold.cuh
Comment thread cpp/src/neighbors/detail/cagra/cagra_merge_scaffold.cuh Outdated
Comment thread cpp/src/neighbors/detail/cagra/cagra_merge_scaffold.cuh
Comment thread cpp/src/neighbors/detail/cagra/cagra_merge_scaffold.cuh Outdated
Comment on lines +399 to +407
if (params.attach_dataset_on_build) {
// CAGRA search assumes 16-byte row alignment (vectorized loads). Consolidate remains dense for
// the scaffold; attach through make_aligned_dataset so unaligned dims are padded.
merged_index.update_dataset(handle, make_aligned_dataset(handle, std::move(dataset), 16));
} else {
using ds_idx_type = typename index<T, IdxT>::dataset_index_type;
merged_index.update_dataset(
handle, std::make_unique<cuvs::neighbors::empty_dataset<ds_idx_type>>(preflight.dim));
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess, we should move the dataset consolidation into a separate place, perhaps as a part of the dataset API. CC @HowardHuang1 to discuss what would be the place for dataset merging under the new API and who should own it?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can make an issue for this, but could we leave it here for now?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adding offline context to the thread:
The dataset merging logic now present is just copying vectors into storage that's now provided by the caller per interface changes that were introduced with the dataset API. The rebuild version of this takes a bitmap to filter them, and the fastener version doesn't.

Comment thread cpp/src/neighbors/detail/cagra/cagra_merge_scaffold.cuh Outdated
landrumb and others added 5 commits August 3, 2026 10:23
Co-authored-by: Artem M. Chirkin <9253178+achirkin@users.noreply.github.com>
Co-authored-by: Artem M. Chirkin <9253178+achirkin@users.noreply.github.com>
@landrumb
landrumb requested a review from dantegd August 4, 2026 16:44
@landrumb
landrumb force-pushed the landrumb/cagra-fastener-merge branch from 1b8558a to 653e8a0 Compare August 4, 2026 20:20
@landrumb
landrumb requested a review from achirkin August 5, 2026 17:50
rapids-bot Bot pushed a commit that referenced this pull request Aug 6, 2026
The graph merge PR (#2352) uses some of the kernels defined by CAGRA, and binary size takes a hit from them not appearing in a TU where they can be shared by both CAGRA and Fastener.

Authors:
  - Ben Landrum (https://github.com/landrumb)

Approvers:
  - Divye Gala (https://github.com/divyegala)

URL: #2412
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature request New feature or request non-breaking Introduces a non-breaking change

Projects

Status: In Progress

Development

Successfully merging this pull request may close these issues.

4 participants