Skip to content

Commit

Permalink
introduce libcugraph wheels (#4804)
Browse files Browse the repository at this point in the history
Replaces #4340, contributes to rapidsai/build-planning#33.

Proposes packaging `libcugraph` as a wheel, which is then re-used by `cugraph-cu{11,12}` and `pylibcugraph-cu{11,12}` wheels.

## Notes for Reviewers

### Benefits of these changes

* smaller wheels (see "Size Changes" below)
  - *no more `pylibcugraph` and `cugraph` both holding copies of libcugraph.so*
* faster compile times
  - *no more re-compiling RAFT, thanks to rapidsai/raft#2531
  - *no more recompiling libcugraph.so in both `pylibcugraph` and `cugraph` wheel builds*
* other benefits mentioned in rapidsai/build-planning#33

### Wheel contents

`libcugraph`:

* `libcugraph.so` (shared library)
* cuGraph headers
* vendored dependencies (`fmt`, `spdlog`, CCCL, `cuco`)

`pylibcugraph`:

* `pylibcugraph` Python / Cython code and compiled Cython extensions

`cugraph`:

* `cugraph` Python / Cython code and compiled Cython extension

### Dependency Flows

In short.... `libcugraph` contains `libcugraph.so` and `libcugraph_c.so` dynamic libraries and the headers to link against it.

* Anything that needs to link against cuGraph at build time pulls in `libcugraph` wheels as a build dependency.
* Anything that needs cuGraph's symbols at runtime pulls it in as a runtime dependency, and calls `libcugraph.load_library()`.

For more details and some flowcharts, see rapidsai/build-planning#33 (comment)

### Size changes (CUDA 12, Python 3.12, x86_64)

| wheel                | num files (before) | num files (this PR) | size (before)  | size (this PR) |
|:---------------:|------------------:|-----------------:|--------------:|-------------:|
| `libcugraph`     |   ---                       |  1762                     | ---                   | 903M       |
| `pylibcugraph` |  190                      |   187                       | 901M              | 2M                 |
| `cugraph`         |  315                      |   313                      | 899M              | 3M                 |
|**TOTAL**          |   **505**         |   **2,262**               | **1,800M**                 | **908M**    |

*NOTES: size = compressed, "before" = 2025-01-13 nightlies*

*This is a cuGraph-specific slice of the table from rapidsai/raft#2531. See that PR for details.*

### How I tested this

These other PRs:

* rapidsai/devcontainers#435
* rapidsai/cugraph-gnn#110

Authors:
  - James Lamb (https://github.com/jameslamb)
  - Ralph Liu (https://github.com/nv-rliu)
  - Bradley Dice (https://github.com/bdice)

Approvers:
  - Brad Rees (https://github.com/BradReesWork)
  - Bradley Dice (https://github.com/bdice)

URL: #4804
  • Loading branch information
jameslamb authored Jan 18, 2025
1 parent 2848cd4 commit 8ebff3b
Show file tree
Hide file tree
Showing 28 changed files with 558 additions and 241 deletions.
27 changes: 25 additions & 2 deletions .github/workflows/build.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,29 @@ jobs:
node_type: "gpu-v100-latest-1"
run_script: "ci/build_docs.sh"
sha: ${{ inputs.sha }}
wheel-build-libcugraph:
secrets: inherit
uses: rapidsai/shared-workflows/.github/workflows/wheels-build.yaml@branch-25.02
with:
build_type: ${{ inputs.build_type || 'branch' }}
branch: ${{ inputs.branch }}
sha: ${{ inputs.sha }}
date: ${{ inputs.date }}
script: ci/build_wheel_libcugraph.sh
node_type: cpu32
wheel-publish-libcugraph:
needs: wheel-build-libcugraph
secrets: inherit
uses: rapidsai/shared-workflows/.github/workflows/wheels-publish.yaml@branch-25.02
with:
build_type: ${{ inputs.build_type || 'branch' }}
branch: ${{ inputs.branch }}
sha: ${{ inputs.sha }}
date: ${{ inputs.date }}
package-name: libcugraph
package-type: cpp
wheel-build-pylibcugraph:
needs: wheel-build-libcugraph
secrets: inherit
uses: rapidsai/shared-workflows/.github/workflows/wheels-build.yaml@branch-25.02
with:
Expand All @@ -76,7 +98,6 @@ jobs:
sha: ${{ inputs.sha }}
date: ${{ inputs.date }}
script: ci/build_wheel_pylibcugraph.sh
node_type: cpu32
wheel-publish-pylibcugraph:
needs: wheel-build-pylibcugraph
secrets: inherit
Expand All @@ -87,8 +108,9 @@ jobs:
sha: ${{ inputs.sha }}
date: ${{ inputs.date }}
package-name: pylibcugraph
package-type: python
wheel-build-cugraph:
needs: wheel-publish-pylibcugraph
needs: wheel-build-pylibcugraph
secrets: inherit
uses: rapidsai/shared-workflows/.github/workflows/wheels-build.yaml@branch-25.02
with:
Expand All @@ -107,3 +129,4 @@ jobs:
sha: ${{ inputs.sha }}
date: ${{ inputs.date }}
package-name: cugraph
package-type: python
13 changes: 11 additions & 2 deletions .github/workflows/pr.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ jobs:
- conda-python-build
- conda-python-tests
- docs-build
- wheel-build-libcugraph
- wheel-build-pylibcugraph
- wheel-tests-pylibcugraph
- wheel-build-cugraph
Expand Down Expand Up @@ -149,14 +150,22 @@ jobs:
arch: "amd64"
container_image: "rapidsai/ci-conda:cuda11.8.0-ubuntu22.04-py3.10"
run_script: "ci/build_docs.sh"
wheel-build-pylibcugraph:
wheel-build-libcugraph:
needs: checks
secrets: inherit
uses: rapidsai/shared-workflows/.github/workflows/wheels-build.yaml@branch-25.02
with:
# build for every combination of arch and CUDA version, but only for the latest Python
matrix_filter: group_by([.ARCH, (.CUDA_VER|split(".")|map(tonumber)|.[0])]) | map(max_by(.PY_VER|split(".")|map(tonumber)))
build_type: pull-request
script: ci/build_wheel_libcugraph.sh
wheel-build-pylibcugraph:
needs: wheel-build-libcugraph
secrets: inherit
uses: rapidsai/shared-workflows/.github/workflows/wheels-build.yaml@branch-25.02
with:
build_type: pull-request
script: ci/build_wheel_pylibcugraph.sh
node_type: cpu32
wheel-tests-pylibcugraph:
needs: [wheel-build-pylibcugraph, changed-files]
secrets: inherit
Expand Down
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ repos:
- id: sphinx-lint
args: ["--enable=all", "--disable=line-too-long"]
- repo: https://github.com/rapidsai/dependency-file-generator
rev: v1.16.0
rev: v1.17.0
hooks:
- id: rapids-dependency-file-generator
args: ["--clean"]
7 changes: 1 addition & 6 deletions build.sh
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#!/bin/bash

# Copyright (c) 2019-2024, NVIDIA CORPORATION.
# Copyright (c) 2019-2025, NVIDIA CORPORATION.

# cugraph build script

Expand Down Expand Up @@ -175,11 +175,6 @@ SKBUILD_EXTRA_CMAKE_ARGS="${EXTRA_CMAKE_ARGS}"
# Replace spaces with semicolons in SKBUILD_EXTRA_CMAKE_ARGS
SKBUILD_EXTRA_CMAKE_ARGS=$(echo ${SKBUILD_EXTRA_CMAKE_ARGS} | sed 's/ /;/g')

# Append `-DFIND_CUGRAPH_CPP=ON` to EXTRA_CMAKE_ARGS unless a user specified the option.
if [[ "${EXTRA_CMAKE_ARGS}" != *"DFIND_CUGRAPH_CPP"* ]]; then
SKBUILD_EXTRA_CMAKE_ARGS="${SKBUILD_EXTRA_CMAKE_ARGS};-DFIND_CUGRAPH_CPP=ON"
fi

# If clean or uninstall targets given, run them prior to any other steps
if hasArg uninstall; then
if [[ "$INSTALL_PREFIX" != "" ]]; then
Expand Down
44 changes: 27 additions & 17 deletions ci/build_wheel.sh
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
#!/bin/bash
# Copyright (c) 2023-2024, NVIDIA CORPORATION.
# Copyright (c) 2023-2025, NVIDIA CORPORATION.

set -euo pipefail

package_name=$1
package_dir=$2
package_type=$3

source rapids-configure-sccache
source rapids-date-string
Expand All @@ -29,22 +30,31 @@ python -m pip wheel \

sccache --show-adv-stats

case "${RAPIDS_CUDA_VERSION}" in
12.*)
EXCLUDE_ARGS=(
--exclude "libcublas.so.12"
--exclude "libcublasLt.so.12"
--exclude "libcurand.so.10"
--exclude "libcusolver.so.11"
--exclude "libcusparse.so.12"
--exclude "libnvJitLink.so.12"
)
;;
11.*)
EXCLUDE_ARGS=()
;;
esac
EXCLUDE_ARGS=(
--exclude "libraft.so"
)

# Avoid picking up dependencies on CUDA wheels that come through
# transitively from 'libraft'.
#
# 'libraft' wheels are responsible for carrying a runtime dependency on
# these based on RAFT's needs.
EXCLUDE_ARGS+=(
--exclude "libcublas.so.12"
--exclude "libcublasLt.so.12"
--exclude "libcurand.so.10"
--exclude "libcusolver.so.11"
--exclude "libcusparse.so.12"
--exclude "libnvJitLink.so.12"
)

if [[ "${package_dir}" != "python/libcugraph" ]]; then
EXCLUDE_ARGS+=(
--exclude "libcugraph_c.so"
--exclude "libcugraph.so"
)
fi

mkdir -p final_dist
python -m auditwheel repair -w final_dist "${EXCLUDE_ARGS[@]}" dist/*
RAPIDS_PY_WHEEL_NAME="${package_name}_${RAPIDS_PY_CUDA_SUFFIX}" rapids-upload-wheels-to-s3 python final_dist
RAPIDS_PY_WHEEL_NAME="${package_name}_${RAPIDS_PY_CUDA_SUFFIX}" rapids-upload-wheels-to-s3 "${package_type}" final_dist
36 changes: 12 additions & 24 deletions ci/build_wheel_cugraph.sh
Original file line number Diff line number Diff line change
@@ -1,37 +1,25 @@
#!/bin/bash
# Copyright (c) 2023-2024, NVIDIA CORPORATION.
# Copyright (c) 2023-2025, NVIDIA CORPORATION.

set -euo pipefail

package_dir="python/cugraph"

RAPIDS_PY_CUDA_SUFFIX="$(rapids-wheel-ctk-name-gen ${RAPIDS_CUDA_VERSION})"

# Download the pylibcugraph wheel built in the previous step and make it
# Download the libcugraph and pylibcugraph wheels built in the previous step and make them
# available for pip to find.
#
# ensure 'cugraph' wheel builds always use the 'pylibcugraph' just built in the same CI run
#
# using env variable PIP_CONSTRAINT is necessary to ensure the constraints
# are used when creating the isolated build environment
CPP_WHEELHOUSE=$(RAPIDS_PY_WHEEL_NAME="pylibcugraph_${RAPIDS_PY_CUDA_SUFFIX}" rapids-download-wheels-from-s3 /tmp/pylibcugraph_dist)
LIBCUGRAPH_WHEELHOUSE=$(RAPIDS_PY_WHEEL_NAME="libcugraph_${RAPIDS_PY_CUDA_SUFFIX}" rapids-download-wheels-from-s3 cpp /tmp/libcugraph_dist)
PYLIBCUGRAPH_WHEELHOUSE=$(RAPIDS_PY_WHEEL_NAME="pylibcugraph_${RAPIDS_PY_CUDA_SUFFIX}" rapids-download-wheels-from-s3 python /tmp/pylibcugraph_dist)

echo "pylibcugraph-${RAPIDS_PY_CUDA_SUFFIX} @ file://$(echo ${CPP_WHEELHOUSE}/pylibcugraph_*.whl)" > ./constraints.txt
export PIP_CONSTRAINT="${PWD}/constraints.txt"

PARALLEL_LEVEL=$(python -c \
"from math import ceil; from multiprocessing import cpu_count; print(ceil(cpu_count()/4))")
case "${RAPIDS_CUDA_VERSION}" in
12.*)
EXTRA_CMAKE_ARGS=";-DUSE_CUDA_MATH_WHEELS=ON"
;;
11.*)
EXTRA_CMAKE_ARGS=";-DUSE_CUDA_MATH_WHEELS=OFF"
;;
esac
cat >> ./constraints.txt <<EOF
libcugraph-${RAPIDS_PY_CUDA_SUFFIX} @ file://$(echo ${LIBCUGRAPH_WHEELHOUSE}/libcugraph_*.whl)
pylibcugraph-${RAPIDS_PY_CUDA_SUFFIX} @ file://$(echo ${PYLIBCUGRAPH_WHEELHOUSE}/pylibcugraph_*.whl)
EOF

export SKBUILD_CMAKE_ARGS="-DDETECT_CONDA_ENV=OFF;-DFIND_CUGRAPH_CPP=OFF${EXTRA_CMAKE_ARGS}"
export SKBUILD_BUILD_TOOL_ARGS="-j${PARALLEL_LEVEL};-l${PARALLEL_LEVEL}"
# Using env variable PIP_CONSTRAINT is necessary to ensure the constraints
# are used when creating the isolated build environment.
export PIP_CONSTRAINT="${PWD}/constraints.txt"

./ci/build_wheel.sh cugraph ${package_dir}
./ci/build_wheel.sh cugraph ${package_dir} python
./ci/validate_wheel.sh ${package_dir} final_dist
34 changes: 34 additions & 0 deletions ci/build_wheel_libcugraph.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
#!/bin/bash
# Copyright (c) 2025, NVIDIA CORPORATION.

set -euo pipefail

package_name="libcugraph"
package_dir="python/libcugraph"

rapids-logger "Generating build requirements"
matrix_selectors="cuda=${RAPIDS_CUDA_VERSION%.*};arch=$(arch);py=${RAPIDS_PY_VERSION};cuda_suffixed=true"

rapids-dependency-file-generator \
--output requirements \
--file-key "py_build_${package_name}" \
--file-key "py_rapids_build_${package_name}" \
--matrix "${matrix_selectors}" \
| tee /tmp/requirements-build.txt

rapids-logger "Installing build requirements"
python -m pip install \
-v \
--prefer-binary \
-r /tmp/requirements-build.txt

rapids-logger "Done build requirements"

# build with '--no-build-isolation', for better sccache hit rate
# 0 really means "add --no-build-isolation" (ref: https://github.com/pypa/pip/issues/5735)
export PIP_NO_BUILD_ISOLATION=0

RAPIDS_PY_CUDA_SUFFIX="$(rapids-wheel-ctk-name-gen ${RAPIDS_CUDA_VERSION})"

./ci/build_wheel.sh libcugraph ${package_dir} cpp
./ci/validate_wheel.sh ${package_dir} final_dist
27 changes: 13 additions & 14 deletions ci/build_wheel_pylibcugraph.sh
Original file line number Diff line number Diff line change
@@ -1,24 +1,23 @@
#!/bin/bash
# Copyright (c) 2023-2024, NVIDIA CORPORATION.
# Copyright (c) 2023-2025, NVIDIA CORPORATION.

set -euo pipefail

package_dir="python/pylibcugraph"

PARALLEL_LEVEL=$(python -c \
"from math import ceil; from multiprocessing import cpu_count; print(ceil(cpu_count()/4))")
RAPIDS_PY_CUDA_SUFFIX="$(rapids-wheel-ctk-name-gen ${RAPIDS_CUDA_VERSION})"

case "${RAPIDS_CUDA_VERSION}" in
12.*)
EXTRA_CMAKE_ARGS=";-DUSE_CUDA_MATH_WHEELS=ON"
;;
11.*)
EXTRA_CMAKE_ARGS=";-DUSE_CUDA_MATH_WHEELS=OFF"
;;
esac
# Download the libcugraph wheel built in the previous step and make it
# available for pip to find.
LIBCUGRAPH_WHEELHOUSE=$(RAPIDS_PY_WHEEL_NAME="libcugraph_${RAPIDS_PY_CUDA_SUFFIX}" rapids-download-wheels-from-s3 cpp /tmp/libcugraph_dist)

export SKBUILD_CMAKE_ARGS="-DDETECT_CONDA_ENV=OFF;-DFIND_CUGRAPH_CPP=OFF${EXTRA_CMAKE_ARGS}"
export SKBUILD_BUILD_TOOL_ARGS="-j${PARALLEL_LEVEL};-l${PARALLEL_LEVEL}"
cat >> ./constraints.txt <<EOF
libcugraph-${RAPIDS_PY_CUDA_SUFFIX} @ file://$(echo ${LIBCUGRAPH_WHEELHOUSE}/libcugraph_*.whl)
EOF

./ci/build_wheel.sh pylibcugraph ${package_dir}
# Using env variable PIP_CONSTRAINT is necessary to ensure the constraints
# are used when creating the isolated build environment.
export PIP_CONSTRAINT="${PWD}/constraints.txt"

./ci/build_wheel.sh pylibcugraph ${package_dir} python
./ci/validate_wheel.sh ${package_dir} final_dist
4 changes: 2 additions & 2 deletions ci/release/update-version.sh
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#!/bin/bash
# Copyright (c) 2018-2024, NVIDIA CORPORATION.
# Copyright (c) 2018-2025, NVIDIA CORPORATION.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
Expand Down Expand Up @@ -57,6 +57,7 @@ DEPENDENCIES=(
dask-cuda
dask-cudf
libcudf
libcugraph
libraft
librmm
pylibcugraph
Expand Down Expand Up @@ -109,4 +110,3 @@ find .devcontainer/ -type f -name devcontainer.json -print0 | while IFS= read -r
done

sed_runner "s/:[0-9][0-9]\.[0-9][0-9]/:${NEXT_SHORT_TAG}/" ./notebooks/README.md
sed_runner "s/branch-[0-9][0-9].[0-9][0-9]/branch-${NEXT_SHORT_TAG}/" ./docs/cugraph/source/nx_cugraph/nx_cugraph.md
4 changes: 3 additions & 1 deletion ci/test_wheel_cugraph.sh
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,13 @@ set -eoxu pipefail
mkdir -p ./dist
RAPIDS_PY_CUDA_SUFFIX="$(rapids-wheel-ctk-name-gen ${RAPIDS_CUDA_VERSION})"
RAPIDS_PY_WHEEL_NAME="cugraph_${RAPIDS_PY_CUDA_SUFFIX}" rapids-download-wheels-from-s3 python ./dist
RAPIDS_PY_WHEEL_NAME="libcugraph_${RAPIDS_PY_CUDA_SUFFIX}" rapids-download-wheels-from-s3 cpp ./local-libcugraph-dep
RAPIDS_PY_WHEEL_NAME="pylibcugraph_${RAPIDS_PY_CUDA_SUFFIX}" rapids-download-wheels-from-s3 python ./local-pylibcugraph-dep

# echo to expand wildcard before adding `[extra]` requires for pip
python -m pip install \
"$(echo ./dist/cugraph*.whl)[test]" \
./local-pylibcugraph-dep/pylibcugraph*.whl
./local-pylibcugraph-dep/pylibcugraph*.whl \
./local-libcugraph-dep/libcugraph*.whl

./ci/test_wheel.sh cugraph
4 changes: 3 additions & 1 deletion ci/test_wheel_pylibcugraph.sh
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@ set -eoxu pipefail
# Download the packages built in the previous step
mkdir -p ./dist
RAPIDS_PY_CUDA_SUFFIX="$(rapids-wheel-ctk-name-gen ${RAPIDS_CUDA_VERSION})"
RAPIDS_PY_WHEEL_NAME="libcugraph_${RAPIDS_PY_CUDA_SUFFIX}" rapids-download-wheels-from-s3 cpp ./local-libcugraph-dep
RAPIDS_PY_WHEEL_NAME="pylibcugraph_${RAPIDS_PY_CUDA_SUFFIX}" rapids-download-wheels-from-s3 python ./dist

# echo to expand wildcard before adding `[extra]` requires for pip
python -m pip install \
"$(echo ./dist/pylibcugraph*.whl)[test]"
"$(echo ./dist/pylibcugraph*.whl)[test]" \
./local-libcugraph-dep/libcugraph*.whl

./ci/test_wheel.sh pylibcugraph
20 changes: 1 addition & 19 deletions ci/validate_wheel.sh
Original file line number Diff line number Diff line change
@@ -1,35 +1,17 @@
#!/bin/bash
# Copyright (c) 2024, NVIDIA CORPORATION.
# Copyright (c) 2024-2025, NVIDIA CORPORATION.

set -euo pipefail

package_dir=$1
wheel_dir_relative_path=$2

RAPIDS_CUDA_MAJOR="${RAPIDS_CUDA_VERSION%%.*}"

# some packages are much larger on CUDA 11 than on CUDA 12
if [[ "${package_dir}" == "python/cugraph" ]] || [[ "${package_dir}" == "python/pylibcugraph" ]]; then
if [[ "${RAPIDS_CUDA_MAJOR}" == "11" ]]; then
PYDISTCHECK_ARGS=(
--max-allowed-size-compressed '1.5G'
)
else
PYDISTCHECK_ARGS=(
--max-allowed-size-compressed '975M'
)
fi
else
PYDISTCHECK_ARGS=()
fi

cd "${package_dir}"

rapids-logger "validate packages with 'pydistcheck'"

pydistcheck \
--inspect \
"${PYDISTCHECK_ARGS[@]}" \
"$(echo ${wheel_dir_relative_path}/*.whl)"

rapids-logger "validate packages with 'twine'"
Expand Down
Loading

0 comments on commit 8ebff3b

Please sign in to comment.