Skip to content

All-neighbors graph on CPU#2313

Open
jinsolp wants to merge 8 commits into
NVIDIA:mainfrom
jinsolp:all-neigh-cpu-graph
Open

All-neighbors graph on CPU#2313
jinsolp wants to merge 8 commits into
NVIDIA:mainfrom
jinsolp:all-neigh-cpu-graph

Conversation

@jinsolp

@jinsolp jinsolp commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Closes #1903

This PR adds support for CPU kNN graph for batched all-neighbors. Passing host matrices for the indices and distances avoids having to fit the entire graph on GPU. Small perf gap and expected peak GPU memory reductions below:

results

@jinsolp jinsolp self-assigned this Jul 10, 2026
@jinsolp jinsolp requested review from a team as code owners July 10, 2026 15:43
@jinsolp jinsolp added improvement Improves an existing functionality non-breaking Introduces a non-breaking change labels Jul 10, 2026

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

PR looks great! Just had 3 comments and a broader concern: batched inner-product results can still be incorrect when similarities are negative if I'm not mistaken. reset_global_matrices uses std::numeric_limits::min(), which is the smallest positive value, so the sentinel can rank ahead of valid negative inner products during the merge. I think switching this to lowest() and adding a centered-data test might be a good idea.

auto distances_view =
direct.second.has_value()
? direct.second.value()
: raft::make_device_matrix<T, IdxT>(this->res, dataset.extent(0), this->k).view();

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.

What do you think about keeping an owning device_matrix alive here when distances weren’t requested? This view points into a temporary destroyed at the end of this statement, so the brute force search below receives a dangling pointer for indices-only builds. Could we retain the matrix in a local owner and add indices-only coverage?

indices.extent(1) == distances.value().extent(1),
"indices matrix and distances matrix has to be the same shape.");
}
if (core_distances.has_value()) {

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 also require core_distances.size() == dataset.extent(0) here? The host path uses this size as num_rows and indexes the distances matrix with it, while the reachability epilogue is indexed by dataset row. A mismatched C++ view can therefore cause out-of-bounds access. What do you think about validating the extent here?

* - A host-resident dataset accepts either host- or device-resident outputs (indices, distances,
* core_distances); a device-resident dataset requires device-resident outputs. All provided
* outputs must share the same memory space.
* - Host-resident outputs never materialize the full [num_rows x k] graph on the GPU.

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.

full_build_host() still allocates full device indices and optional distances when n_clusters == 1, no? So the current wording overstates the memory reduction a little bit.

What do you think about narrowing this guarantee to batched builds (n_clusters > 1)?

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

Labels

improvement Improves an existing functionality non-breaking Introduces a non-breaking change

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[FEA] Improve batched all-neighbors given CPU indices/distances

2 participants