Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add utility for memory efficient maximum pairwise distance computation with GPU support #838

Open
wants to merge 2 commits into
base: branch-25.04
Choose a base branch
from

Conversation

grlee77
Copy link
Contributor

@grlee77 grlee77 commented Feb 16, 2025

The maximum Feret diameter computations compute pairwise distances between all points which can lead to out of memory errors if the number of points on the object boundary is large. (The memory used is quadratic in the number of points).

This MR implements a block-wise version that retains efficiency, but has much lower memory requirements. It also handles checking for optional GPU acceleration via CuPy's optional cuVS/pylibraft dependencies. CPU fallback is done if those are not available.

Compute maximal pairwise distance without storing all distances
Provides fallback to CPU implementation of optional cuVS/pylibraft dependency is not available
@grlee77 grlee77 added improvement Improves an existing functionality non-breaking Introduces a non-breaking change labels Feb 16, 2025
@grlee77 grlee77 added this to the v25.04.00 milestone Feb 16, 2025
@grlee77 grlee77 self-assigned this Feb 16, 2025
@grlee77 grlee77 requested a review from a team as a code owner February 16, 2025 18:00
Copy link
Contributor

@gigony gigony left a comment

Choose a reason for hiding this comment

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

Thanks @grlee77 for this update! I left some minor comments on this.

if _distance_on_cpu:
warnings.warn(
"cuVS >= 25.02 or pylibraft < 24.12 must be installed to use "
"GPU-accelerated pairwaise distance computations. Falling back "
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
"GPU-accelerated pairwaise distance computations. Falling back "
"GPU-accelerated pairwise distance computations. Falling back "

Internally, calls to cdist will be made with subsets of coords where
the subset size is (coords_per_block, ndim).
compute_argmax : bool, optional
If True, the value of the coordate indices corresponding to the maxima
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
If True, the value of the coordate indices corresponding to the maxima
If True, the value of the cooridate indices corresponding to the maxima

requirement. The memory used at runtime will be proportional to
``coords_per_block**2``.

A block size of >= 2000 is recommended to overhead poor GPU resource usage
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
A block size of >= 2000 is recommended to overhead poor GPU resource usage
A block size of >= 2000 is recommended to avoid poor GPU resource usage

)
current_output = temp
else:
# omit out= for the last block as size may be
Copy link
Contributor

Choose a reason for hiding this comment

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

This comment sentence doesn't seem to be complete.

Comment on lines +48 to +49
coords : np.ndarray (num_points, ndim)
The coordinates to process.
Copy link
Contributor

Choose a reason for hiding this comment

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

The code converts input coordinates to float32 by default, which could lead to precision loss. I think it would be good to note that this coords would be converted to float32 data type.

Comment on lines +108 to +109

num_coords, _ = coords.shape
Copy link
Contributor

Choose a reason for hiding this comment

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

Input validation might be needed here.

Suggested change
num_coords, _ = coords.shape
if not isinstance(coords, (np.ndarray, cp.ndarray)):
raise TypeError("coords must be a numpy or cupy array")
if coords.ndim != 2:
raise ValueError(
f"coords must be a 2-dimensional array, got shape {coords.shape}"
)
num_coords, _ = coords.shape

"to SciPy-based CPU implementation."
)
xp = np
coords = cp.asnumpy(coords)
Copy link
Contributor

Choose a reason for hiding this comment

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

We don't need to use numpy for here?

Suggested change
coords = cp.asnumpy(coords)
coords = np.asnumpy(coords)


Parameters
----------
coords : np.ndarray (num_points, ndim)
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
coords : np.ndarray (num_points, ndim)
coords : numpy.ndarray or cupy.ndarray of shape (num_points, ndim)

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
Status: No status
Development

Successfully merging this pull request may close these issues.

2 participants