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

[FEA]: Introduce Python module with CCCL headers #3201

Open
wants to merge 23 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 8 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
daab580
Add cccl/python/cuda_cccl directory and use from cuda_parallel, cuda_…
rwgk Dec 12, 2024
ef9d5f4
Run `copy_cccl_headers_to_aude_include()` before `setup()`
rwgk Dec 20, 2024
bc116dc
Create python/cuda_cccl/cuda/_include/__init__.py, then simply import…
rwgk Dec 20, 2024
2913ae0
Add cuda.cccl._version exactly as for cuda.cooperative and cuda.parallel
rwgk Dec 20, 2024
7dbb82b
Bug fix: cuda/_include only exists after shutil.copytree() ran.
rwgk Dec 20, 2024
0703901
Use `f"cuda-cccl @ file://{cccl_path}/python/cuda_cccl"` in setup.py
rwgk Dec 20, 2024
fc0e543
Remove CustomBuildCommand, CustomWheelBuild in cuda_parallel/setup.py…
rwgk Dec 20, 2024
2e64345
Replace := operator (needs Python 3.8+)
rwgk Dec 20, 2024
82467cd
Merge branch 'main' into pip-cuda-cccl
rwgk Dec 20, 2024
f13a96b
Fix oversights: remove `pip3 install ./cuda_cccl` lines from README.md
rwgk Dec 20, 2024
9ed6036
Restore original README.md: `pip3 install -e` now works on first pass.
rwgk Dec 20, 2024
c9a4d96
cuda_cccl/README.md: FOR INTERNAL USE ONLY
rwgk Dec 20, 2024
df943c0
Remove `$pymajor.$pyminor.` prefix in cuda_cccl _version.py (as sugge…
rwgk Dec 20, 2024
40c8389
Modernize pyproject.toml, setup.py
rwgk Dec 21, 2024
e3c7867
Install CCCL headers under cuda.cccl.include
rwgk Dec 21, 2024
acbd477
Merge branch 'main' into pip-cuda-cccl
rwgk Dec 21, 2024
06f575f
Factor out cuda_cccl/cuda/cccl/include_paths.py
rwgk Dec 21, 2024
e747768
Reuse cuda_cccl/cuda/cccl/include_paths.py from cuda_cooperative
rwgk Dec 21, 2024
499b191
Merge branch 'main' into pip-cuda-cccl
rwgk Dec 21, 2024
62ce2d3
Add missing Copyright notice.
rwgk Dec 21, 2024
65c5a15
Add missing __init__.py (cuda.cccl)
rwgk Dec 21, 2024
bffece6
Add `"cuda.cccl"` to `autodoc.mock_imports`
rwgk Dec 21, 2024
585447c
Move cuda.cccl.include_paths into function where it is used. (Attempt…
rwgk Dec 22, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 7 additions & 5 deletions ci/test_python.sh
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,12 @@ fail_if_no_gpu
readonly prefix="${BUILD_DIR}/python/"
export PYTHONPATH="${prefix}:${PYTHONPATH:-}"

pushd ../python/cuda_cccl >/dev/null

run_command "⚙️ Pip install cuda_cccl" pip install --force-reinstall --upgrade --target "${prefix}" .

popd >/dev/null

pushd ../python/cuda_cooperative >/dev/null

run_command "⚙️ Pip install cuda_cooperative" pip install --force-reinstall --upgrade --target "${prefix}" .[test]
Expand All @@ -20,11 +26,7 @@ popd >/dev/null

pushd ../python/cuda_parallel >/dev/null

# Temporarily install the package twice to populate include directory as part of the first installation
# and to let manifest discover these includes during the second installation. Do not forget to remove the
# second installation after https://github.com/NVIDIA/cccl/issues/2281 is addressed.
run_command "⚙️ Pip install cuda_parallel once" pip install --force-reinstall --upgrade --target "${prefix}" .[test]
run_command "⚙️ Pip install cuda_parallel twice" pip install --force-reinstall --upgrade --target "${prefix}" .[test]
run_command "⚙️ Pip install cuda_parallel" pip install --force-reinstall --upgrade --target "${prefix}" .[test]
run_command "🚀 Pytest cuda_parallel" python -m pytest -v ./tests

popd >/dev/null
Expand Down
2 changes: 2 additions & 0 deletions ci/update_version.sh
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ CUB_CMAKE_VERSION_FILE="lib/cmake/cub/cub-config-version.cmake"
LIBCUDACXX_CMAKE_VERSION_FILE="lib/cmake/libcudacxx/libcudacxx-config-version.cmake"
THRUST_CMAKE_VERSION_FILE="lib/cmake/thrust/thrust-config-version.cmake"
CUDAX_CMAKE_VERSION_FILE="lib/cmake/cudax/cudax-config-version.cmake"
CUDA_CCCL_VERSION_FILE="python/cuda_cccl/cuda/cccl/_version.py"
CUDA_COOPERATIVE_VERSION_FILE="python/cuda_cooperative/cuda/cooperative/_version.py"
CUDA_PARALLEL_VERSION_FILE="python/cuda_parallel/cuda/parallel/_version.py"

Expand Down Expand Up @@ -110,6 +111,7 @@ update_file "$CUDAX_CMAKE_VERSION_FILE" "set(cudax_VERSION_MAJOR \([0-9]\+\))" "
update_file "$CUDAX_CMAKE_VERSION_FILE" "set(cudax_VERSION_MINOR \([0-9]\+\))" "set(cudax_VERSION_MINOR $minor)"
update_file "$CUDAX_CMAKE_VERSION_FILE" "set(cudax_VERSION_PATCH \([0-9]\+\))" "set(cudax_VERSION_PATCH $patch)"

update_file "$CUDA_CCCL_VERSION_FILE" "^__version__ = \"\([0-9.]\+\)\"" "__version__ = \"$pymajor.$pyminor.$major.$minor.$patch\""
Copy link
Member

Choose a reason for hiding this comment

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

I think this is wrong, we need the headers to track C++ versions

Suggested change
update_file "$CUDA_CCCL_VERSION_FILE" "^__version__ = \"\([0-9.]\+\)\"" "__version__ = \"$pymajor.$pyminor.$major.$minor.$patch\""
update_file "$CUDA_CCCL_VERSION_FILE" "^__version__ = \"\([0-9.]\+\)\"" "__version__ = \"$major.$minor.$patch\""

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done: commit df943c0

update_file "$CUDA_COOPERATIVE_VERSION_FILE" "^__version__ = \"\([0-9.]\+\)\"" "__version__ = \"$pymajor.$pyminor.$major.$minor.$patch\""
update_file "$CUDA_PARALLEL_VERSION_FILE" "^__version__ = \"\([0-9.]\+\)\"" "__version__ = \"$pymajor.$pyminor.$major.$minor.$patch\""

Expand Down
2 changes: 2 additions & 0 deletions python/cuda_cccl/.gitignore
Copy link
Member

Choose a reason for hiding this comment

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

Q: Is it possible that we consolidate .gitignore files at the root directory and not have independent ones per sub dir...?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I created #3212 to look into this later.

Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
cuda/_include
*egg-info
File renamed without changes.
11 changes: 11 additions & 0 deletions python/cuda_cccl/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# `cuda.cccl`: Experimental CUDA Core Compute Library Python module with CCCL headers
Copy link
Contributor

Choose a reason for hiding this comment

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

Nit: we should consider the name cuda.cccl_headers for clarity.

Copy link
Member

Choose a reason for hiding this comment

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

I have reservation on this, if considering the mirroring to conda packages (#3201 (comment)).

Copy link
Contributor

Choose a reason for hiding this comment

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

Ah, I waasn't aware there's a conda package called cuda-cccl already. Agree, we should be consistent with that.


## Documentation

Please visit the documentation here: https://nvidia.github.io/cccl/python.html.

## Local development

```bash
pip3 install .
```
Copy link
Contributor

Choose a reason for hiding this comment

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

Perhaps it's appropriate to document that this package is currently for internal use only and not meant to be used/installed explicitly.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done: c9a4d96

7 changes: 7 additions & 0 deletions python/cuda_cccl/cuda/cccl/_version.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED.
#
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

# This file is generated by ci/update_version.sh
# Do not edit this file manually.
__version__ = "0.1.2.8.0"
7 changes: 7 additions & 0 deletions python/cuda_cccl/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED.
#
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

[build-system]
requires = ["packaging", "setuptools>=61.0.0", "wheel"]
build-backend = "setuptools.build_meta"
57 changes: 57 additions & 0 deletions python/cuda_cccl/setup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED.
#
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

import os
import shutil

from setuptools import setup, find_namespace_packages


project_path = os.path.abspath(os.path.dirname(__file__))
cccl_path = os.path.abspath(os.path.join(project_path, "..", ".."))
cccl_headers = [["cub", "cub"], ["libcudacxx", "include"], ["thrust", "thrust"]]
__version__ = None
with open(os.path.join(project_path, "cuda", "cccl", "_version.py")) as f:
exec(f.read())
assert __version__ is not None
ver = __version__
del __version__
Copy link
Member

Choose a reason for hiding this comment

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

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done: commit 40c8389



with open("README.md") as f:
long_description = f.read()
Copy link
Member

@leofang leofang Dec 20, 2024

Choose a reason for hiding this comment

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

nit: this can be moved to pyproject.toml too, ex:
https://github.com/NVIDIA/cuda-python/blob/33b7366e308201f3bca8206ae331e399ac1b3379/cuda_core/pyproject.toml#L65
(in pyproject.toml, readme is the new preferred name over long_description)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done: commit 40c8389



def copy_cccl_headers_to_cuda_include():
inc_path = os.path.join(project_path, "cuda", "_include")
Copy link
Member

Choose a reason for hiding this comment

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

Important: Can we please establish the right layout before merging? There is a new wheel layout being communicated internally. This should be something like

Suggested change
inc_path = os.path.join(project_path, "cuda", "_include")
inc_path = os.path.join(project_path, "cccl", "include")

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done: commit e3c7867

I'm not sure about two aspects:

  • Did you mean to suggest site-packages/cccl/include/ as the install directory? — I decided to make this site-packages/cuda/cccl/include/. It seemed odd to me to have it outside the cuda subdir.

  • By accident I discovered that all cuda.cooperative unit tests pass without the CCCL headers. @gevtushenko for comment.

Copy link
Collaborator

Choose a reason for hiding this comment

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

By accident I discovered that all cuda.cooperative unit tests pass without the CCCL headers. @gevtushenko for comment.

@rwgk we add path to CUDA Toolkit (CTK) headers here:

cuda_include_path = os.path.join(get_cuda_path(), "include")

CTK provides CUB headers as well. Tests likely pass because they use CTK version of CUB. We should add a static assert somewhere in cuda.cooperative to check that we use version of CUB that cuda.cooperative was "build" with.

for proj_dir, header_dir in cccl_headers:
src_path = os.path.abspath(os.path.join(cccl_path, proj_dir, header_dir))
dst_path = os.path.join(inc_path, proj_dir)
if os.path.exists(dst_path):
shutil.rmtree(dst_path)
shutil.copytree(src_path, dst_path)
init_py_path = os.path.join(inc_path, "__init__.py")
with open(init_py_path, "w") as f:
print("# Intentionally empty.", file=f)


copy_cccl_headers_to_cuda_include()

setup(
name="cuda-cccl",
version=ver,
description="Experimental Package with CCCL headers to support JIT compilation",
long_description=long_description,
long_description_content_type="text/markdown",
author="NVIDIA Corporation",
classifiers=[
"Programming Language :: Python :: 3 :: Only",
"Environment :: GPU :: NVIDIA CUDA",
],
packages=find_namespace_packages(include=["cuda.*"]),
python_requires=">=3.9",
include_package_data=True,
license="Apache-2.0 with LLVM exception",
license_files=("../../LICENSE",),
)
1 change: 0 additions & 1 deletion python/cuda_cooperative/.gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,2 @@
cuda/_include
env
*egg-info
13 changes: 11 additions & 2 deletions python/cuda_cooperative/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,16 @@ Please visit the documentation here: https://nvidia.github.io/cccl/python.html.

## Local development

First-time installation:

```bash
pip3 install ./cuda_cccl
pip3 install ./cuda_cooperative[test]
pytest -v ./cuda_cooperative/tests/
```

For faster iterative development:

```bash
pip3 install -e .[test]
pytest -v ./tests/
pip3 install -e ./cuda_cooperative[test]
```
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,6 @@ def compile_impl(cpp, cc, rdc, code, nvrtc_path, nvrtc_version):
check_in("code", code, ["lto", "ptx"])

with pkg_resources.path("cuda", "_include") as include_path:
# Using `.parent` for compatibility with pip install --editable:
include_path = pkg_resources.files("cuda.cooperative").parent.joinpath(
"_include"
)
cub_path = include_path
thrust_path = include_path
libcudacxx_path = os.path.join(include_path, "libcudacxx")
Expand Down
26 changes: 2 additions & 24 deletions python/cuda_cooperative/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,8 @@
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

import os
import shutil

from setuptools import Command, setup, find_namespace_packages
from setuptools import setup, find_namespace_packages
from setuptools.command.build_py import build_py
from wheel.bdist_wheel import bdist_wheel

Expand All @@ -27,35 +26,14 @@

class CustomBuildCommand(build_py):
def run(self):
self.run_command("package_cccl")
build_py.run(self)


class CustomWheelBuild(bdist_wheel):
def run(self):
self.run_command("package_cccl")
super().run()


class PackageCCCLCommand(Command):
description = "Generate additional files"
user_options = []

def initialize_options(self):
pass

def finalize_options(self):
pass

def run(self):
for proj_dir, header_dir in cccl_headers:
src_path = os.path.abspath(os.path.join(cccl_path, proj_dir, header_dir))
dst_path = os.path.join(project_path, "cuda", "_include", proj_dir)
if os.path.exists(dst_path):
shutil.rmtree(dst_path)
shutil.copytree(src_path, dst_path)


setup(
name="cuda-cooperative",
version=ver,
Expand All @@ -70,6 +48,7 @@ def run(self):
packages=find_namespace_packages(include=["cuda.*"]),
python_requires=">=3.9",
install_requires=[
f"cuda-cccl @ file://{cccl_path}/python/cuda_cccl",
"numba>=0.60.0",
"pynvjitlink-cu12>=0.2.4",
"cuda-python",
Expand All @@ -82,7 +61,6 @@ def run(self):
]
},
cmdclass={
"package_cccl": PackageCCCLCommand,
"build_py": CustomBuildCommand,
"bdist_wheel": CustomWheelBuild,
},
Expand Down
1 change: 0 additions & 1 deletion python/cuda_parallel/.gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
cuda/_include
env
*egg-info
*so
1 change: 0 additions & 1 deletion python/cuda_parallel/MANIFEST.in

This file was deleted.

13 changes: 11 additions & 2 deletions python/cuda_parallel/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,16 @@ Please visit the documentation here: https://nvidia.github.io/cccl/python.html.

## Local development

First-time installation:

```bash
pip3 install ./cuda_cccl
pip3 install ./cuda_parallel[test]
Copy link
Contributor

Choose a reason for hiding this comment

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

Can this be editable if necessary? Wouldn't a regular install here and then an editable install below would lead to two copies of the package in the environment?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It can! Thanks for asking. Previously I was incorrectly thinking that one pass without -e is required. I tried again fresh and it turns out it actually does work on first pass.

I restored the original README.md: commit 9ed6036

Wouldn't a regular install here and then an editable install below would lead to two copies of the package in the environment?

From what I can tell, the 2nd install clobbers the previous one.

pytest -v ./cuda_parallel/tests/
```

For faster iterative development:

```bash
pip3 install -e .[test]
pytest -v ./tests/
pip3 install -e ./cuda_parallel[test]
```
Original file line number Diff line number Diff line change
Expand Up @@ -52,13 +52,13 @@ def get_bindings() -> ctypes.CDLL:

@lru_cache()
def get_paths() -> List[bytes]:
with as_file(files("cuda.parallel")) as f:
# Using `.parent` for compatibility with pip install --editable:
cub_include_path = str(f.parent / "_include")
with as_file(files("cuda._include")) as f:
cub_include_path = str(f)
thrust_include_path = cub_include_path
libcudacxx_include_path = str(os.path.join(cub_include_path, "libcudacxx"))
cuda_include_path = None
if cuda_path := _get_cuda_path():
cuda_path = _get_cuda_path()
if cuda_path:
cuda_include_path = str(os.path.join(cuda_path, "include"))
paths = [
f"-I{path}".encode()
Expand Down
47 changes: 7 additions & 40 deletions python/cuda_parallel/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,10 @@
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

import os
import shutil
import subprocess

from setuptools import Command, Extension, setup, find_namespace_packages
from setuptools.command.build_py import build_py
from setuptools import Extension, setup, find_namespace_packages
from setuptools.command.build_ext import build_ext
from wheel.bdist_wheel import bdist_wheel


project_path = os.path.abspath(os.path.dirname(__file__))
Expand All @@ -27,38 +24,6 @@
long_description = f.read()


class CustomBuildCommand(build_py):
def run(self):
self.run_command("package_cccl")
build_py.run(self)


class CustomWheelBuild(bdist_wheel):
def run(self):
self.run_command("package_cccl")
super().run()


class PackageCCCLCommand(Command):
description = "Generate additional files"
user_options = []

def initialize_options(self):
pass

def finalize_options(self):
pass

def run(self):
for proj_dir, header_dir in cccl_headers:
src_path = os.path.abspath(os.path.join(cccl_path, proj_dir, header_dir))
# TODO Extract cccl headers into a standalone package
dst_path = os.path.join(project_path, "cuda", "_include", proj_dir)
if os.path.exists(dst_path):
shutil.rmtree(dst_path)
shutil.copytree(src_path, dst_path)


class CMakeExtension(Extension):
def __init__(self, name):
super().__init__(name, sources=[])
Expand Down Expand Up @@ -100,7 +65,12 @@ def build_extension(self, ext):
],
packages=find_namespace_packages(include=["cuda.*"]),
python_requires=">=3.9",
install_requires=["numba>=0.60.0", "cuda-python", "jinja2"],
install_requires=[
f"cuda-cccl @ file://{cccl_path}/python/cuda_cccl",
"numba>=0.60.0",
"cuda-python",
"jinja2",
],
extras_require={
"test": [
"pytest",
Expand All @@ -109,9 +79,6 @@ def build_extension(self, ext):
]
},
cmdclass={
"package_cccl": PackageCCCLCommand,
"build_py": CustomBuildCommand,
"bdist_wheel": CustomWheelBuild,
"build_ext": BuildCMakeExtension,
},
ext_modules=[CMakeExtension("cuda.parallel.experimental.cccl.c")],
Expand Down
Loading