Skip to content

Conversation

@rhdong
Copy link
Member

@rhdong rhdong commented Feb 20, 2025

@rhdong rhdong added feature request New feature or request non-breaking Introduces a non-breaking change labels Feb 20, 2025
@rhdong rhdong requested review from achirkin and cjnolet February 20, 2025 20:14
@rhdong rhdong requested a review from a team as a code owner February 20, 2025 20:14
@github-actions github-actions bot added the cpp label Feb 20, 2025
@achirkin
Copy link
Contributor

I may have missed some prior discussion, but the cagra-specific logical merge seems a little bit artificial for me.
If I understand this correctly, you essentially implement a multi-index search. Then, why don't we go one step further and make it independent of a particular index implementation? Moreover, it looks like much of this functionality is implemented already in multi-gpu index. Maybe we can generalize that one a bit to decouple "multi-index" from "multi-gpu" aspect? Optionally, one can also combine different index types and erase the upstream index type like we do in dynamic batching index.

@cjnolet
Copy link
Member

cjnolet commented Feb 21, 2025

I may have missed some prior discussion, but the cagra-specific logical merge seems a little bit artificial for me.

@achirkin you and I haven't discussed this yet but this feature is critical for certain database architectures like Lucene, which are variants of the log-structured-merge pattern but build indexes immediately after segments (flat files containing vectors) are created, rather than merging the segments together before building the indexes. This causes Lucene to have to first merge many tiny CAGRA indexes together, but eventually it'll end up merging very large indexes together. It is this latter case thart we care about doing a logical merge. It's more efficient to create a single cagra index with many (potentially hundreds) of tiny cagra indexes but when they reach a certain size, it's actually more efficient to logically merge them; meaning we essentially wrap them as if they were shards and broadcast the query to all the shards during search.

I agree this is similar in theory to the multi-gpu sharding and perhaps there is some code to be reused there. The next step for the merge() API is to be able to implement a SMART merge strategy, whereby we can more efficiently merge two cagra graphs together without having to rebuild from scratch, and that's ultimately the strategy we have discussed offline in more detail. The problem is that Lucene can make use of this today, so it gives us time to work towards the SMART option.

Then, why don't we go one step further and make it independent of a particular index implementation?

I do agree with this- it doesn't have to be (and ideally wouldn't be) specifically for CAGRA, though that just happens to be the index we care about today because it unblocks Lucene.

@achirkin
Copy link
Contributor

I totally agree and don't doubt the usefulness of logical merge.
I've just pointed out that I think we can accomplish a more general solution (supporting any index type) with actually less code by re-using/adjusting what we already have in multi-index/dynamic batching.

@rhdong rhdong closed this Mar 13, 2025
@rhdong rhdong reopened this Mar 13, 2025
@rhdong
Copy link
Member Author

rhdong commented Mar 13, 2025

I may have missed some prior discussion, but the cagra-specific logical merge seems a little bit artificial for me. If I understand this correctly, you essentially implement a multi-index search. Then, why don't we go one step further and make it independent of a particular index implementation? Moreover, it looks like much of this functionality is implemented already in multi-gpu index. Maybe we can generalize that one a bit to decouple "multi-index" from "multi-gpu" aspect? Optionally, one can also combine different index types and erase the upstream index type like we do in dynamic batching index.

Hi @achirkin , sorry for the late response to your comments here. I studied the code of SNMG, which is a super useful feature. When I tried to reuse some code in it, I found the caller has to work with locker, nccl, which is unnecessary in the logical merge of CAGRA. I'd like to keep the simple implementation if you agree. Many thanks! cc: @cjnolet

@rhdong
Copy link
Member Author

rhdong commented Mar 18, 2025

Hi @cjnolet @achirkin, could you please merge it if there are no more comments? Many thanks!

@cjnolet
Copy link
Member

cjnolet commented Apr 25, 2025

When I tried to reuse some code in it, I found the caller has to work with locker, nccl, which is unnecessary in the logical merge of CAGRA. I'd like to keep the simple implementation if you agree. Many thanks! cc: @cjnolet

@rhdong I don't think the ask here is that you use the multi-gpu apis, but rather that we have a more consistent and general means of being able to support wrapper types that can work with any index type and avoid the need to, for example, implement a separate composite_index type for each index type within cuVS. Dynamic batching and the multi-gpu apis are two examples of where we have a single index that can be used by any other index. This is ultimately where we should strive to be.

@rhdong
Copy link
Member Author

rhdong commented Apr 30, 2025

When I tried to reuse some code in it, I found the caller has to work with locker, nccl, which is unnecessary in the logical merge of CAGRA. I'd like to keep the simple implementation if you agree. Many thanks! cc: @cjnolet

@rhdong I don't think the ask here is that you use the multi-gpu apis, but rather that we have a more consistent and general means of being able to support wrapper types that can work with any index type and avoid the need to, for example, implement a separate composite_index type for each index type within cuVS. Dynamic batching and the multi-gpu apis are two examples of where we have a single index that can be used by any other index. This is ultimately where we should strive to be.

Hi @cjnolet @achirkin , just want to clarify before moving forward, what you're suggesting is lifting up the composite_index to the upper level, like by moving it to common.hpp, and using the cuvs::neighbour::index as array item type, am I right? Many thanks!

@cjnolet
Copy link
Member

cjnolet commented May 7, 2025

Hi @cjnolet @achirkin , just want to clarify before moving forward, what you're suggesting is lifting up the composite_index to the upper level, like by moving it to common.hpp, and using the cuvs::neighbour::index as array item type, am I right? Many thanks!

Yes, though I think we could address this in a follow-up PR and merge this change in the meantime. We really need CAGRA merge capability initially so that we can unblock our Lucene friends. @rhdong can you create a new github issue to follow-up with the first-class formal generalized composite_index?

The other big reason why centralizing this composite index is important is because we want it to work out of the box with the other APIs that work with general indexes (such as the snmg apis).

@github-actions github-actions bot added the CMake label May 27, 2025
@rhdong rhdong requested a review from cjnolet May 27, 2025 02:57
@rhdong
Copy link
Member Author

rhdong commented May 27, 2025

Hi @cjnolet , I’ve provided a multi-stream implementation, but it appears to show no performance improvement. (Sorry for the accidental force push during this). According to your suggestion, I’ve submitted an issue(#946) and will continue working on further optimizations.

}

raft::resources composite_handle(handle_);
size_t n_streams = std::min(wrapped_indices.size(), size_t(8));
Copy link
Member

Choose a reason for hiding this comment

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

out of curiosity, why 8 here? I don't think we need to hold up the release for this, but can you at least create a Github issue to make this configurable (or to find a good reasonable default which can be overridden)?

Copy link
Member Author

Choose a reason for hiding this comment

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

I just changed it to be equal to wrapped_indices.size()

@rhdong rhdong requested a review from cjnolet May 28, 2025 17:16
@cjnolet
Copy link
Member

cjnolet commented May 28, 2025

/merge

@rapids-bot rapids-bot bot merged commit e00fabe into rapidsai:branch-25.06 May 28, 2025
75 checks passed
copy-pr-bot bot pushed a commit that referenced this pull request Jun 3, 2025
mythrocks pushed a commit to mythrocks/cuvs that referenced this pull request Jun 3, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CMake cpp feature request New feature or request non-breaking Introduces a non-breaking change Python

Development

Successfully merging this pull request may close these issues.

3 participants