Skip to content

Roaring Bitmap Filter in CAGRA - #2446

Open
divyegala wants to merge 10 commits into
NVIDIA:release/26.10from
divyegala:roaring-filter
Open

Roaring Bitmap Filter in CAGRA#2446
divyegala wants to merge 10 commits into
NVIDIA:release/26.10from
divyegala:roaring-filter

Conversation

@divyegala

@divyegala divyegala commented Aug 15, 2026

Copy link
Copy Markdown
Contributor

This PR uses cuco's Roaring Bitmap filter implementation to work with CAGRA search. Currently, it only supports single allowlist construction.

Benchmarks on GIST 1M, measuring single-batch search latency + filter construction time:

Allowed Cardinality Bitset total (ms) CSR UDF total (ms) Roaring total (ms)
0.1% 1,000 5.106 5.275 6.042
1% 10,000 1.209 1.553 1.545
10% 100,000 0.814 4.015 1.297
50% 500,000 1.199 17.801 2.146
90% 900,000 1.512 32.017 2.384
100% 1,000,000 1.530 35.768 2.552

Filter sizes:

Allowed Cardinality Bitset CSR UDF Roaring
0.1% 1,000 122.07 KiB 3.92 KiB 2.22 KiB
1% 10,000 122.07 KiB 39.08 KiB 19.80 KiB
10% 100,000 122.07 KiB 390.64 KiB 123.58 KiB
50% 500,000 122.07 KiB 1,953.14 KiB 128.27 KiB
90% 900,000 122.07 KiB 3,515.64 KiB 128.27 KiB
100% 1,000,000 122.07 KiB 3,906.27 KiB 128.27 KiB

@copy-pr-bot

copy-pr-bot Bot commented Aug 15, 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.

@copy-pr-bot

copy-pr-bot Bot commented Sep 2, 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.

@divyegala
divyegala marked this pull request as ready for review September 2, 2026 19:49
@divyegala
divyegala requested review from a team as code owners September 2, 2026 19:49
@divyegala divyegala added feature request New feature or request non-breaking Introduces a non-breaking change labels Sep 2, 2026
@divyegala

Copy link
Copy Markdown
Contributor Author

/ok to test 1529d8f

@dantegd dantegd 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.

Should we explicitly limit Roaring filters to direct cagra::search for now? I don’t think the query mapping works cleanly with dynamic batching because CAGRA may receive either max_batch_size or the actual batch size. Tiered search also passes the same filter to CAGRA and brute-force partitions with different row domains, and brute force doesn’t support Roaring. Since cagra::merge already rejects this filter type, would it make sense to reject or document these paths too, unless they’re intended to be supported? What do you think?

Comment thread cpp/cmake/patches/cuco_override.json Outdated
tenant_views.push_back(allowlist.view());
}
cuvs::neighbors::filtering::roaring_filter roaring_filter(res, tenant_views);
auto roaring_result = search(roaring_filter, 2.0f / 3.0f);

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.

Could we clarify the stream semantics here? Construction may return while serialization and the device-reference copy are still queued. Using the result on the same stream is safe, but another stream needs an explicit dependency, like the event handoff in the test. The current comment explains how long the input must remain valid, but not when the returned view is safe to use.

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.

We commit to stream ordered synchronization guarantees on raft::resources. Is that not already true here? If the host returns execution, it is safe to use.

Comment thread cpp/tests/neighbors/roaring_allowlist.cu
auto& sample_filter =
dynamic_cast<const cuvs::neighbors::filtering::roaring_filter&>(sample_filter_ref);
RAFT_EXPECTS(sample_filter.valid(), "roaring_filter must be initialized before search.");
RAFT_EXPECTS(sample_filter.num_queries() == static_cast<std::size_t>(queries.extent(0)),

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.

Do we expect Roaring filters to work through dynamic batching as well? Dynamic batching may call CAGRA with either max_batch_size or the actual batch size, so this equality can fail depending on the dispatch mode. If that path isn’t supported yet, should we document it or reject the filter earlier?

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.

Let's reject it

{
auto minimum_cardinality = dataset_rows;
for (auto const& allowlist : allowlists) {
minimum_cardinality = std::min(minimum_cardinality, allowlist.cardinality());

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.

Is using the minimum cardinality intentional here? One very small or empty allowlist makes the batch-wide filtering rate approach 0.999, which can substantially increase the MULTI_CTA work for every query in the batch. Would it be worth adding coverage for mixed-cardinality batches or documenting this tradeoff?

@divyegala divyegala Sep 10, 2026

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.

This is documented, I ran into this problem myself. The right way to do this for users it to set search_params::filtering_rate so that it is a CAGRA search specific hint, and users can tradeoff between latency and recall.

@divyegala
divyegala changed the base branch from main to release/26.10 September 10, 2026 17:50
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

None yet

Development

Successfully merging this pull request may close these issues.

3 participants