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 all 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__ = \"$major.$minor.$patch\""
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
1 change: 1 addition & 0 deletions docs/repo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -347,6 +347,7 @@ autodoc.mock_imports = [
"numba",
"pynvjitlink",
"cuda.bindings",
"cuda.cccl",
"llvmlite",
"numpy",
]
Expand Down
3 changes: 3 additions & 0 deletions python/cuda_cccl/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
LICENSE
cuda/cccl/include
*egg-info
1 change: 1 addition & 0 deletions python/cuda_cccl/MANIFEST.in
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
recursive-include cuda/cccl/include *
3 changes: 3 additions & 0 deletions python/cuda_cccl/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
## Note

This package is currently FOR INTERNAL USE ONLY and not meant to be used/installed explicitly.
7 changes: 7 additions & 0 deletions python/cuda_cccl/cuda/cccl/__init__.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

from cuda.cccl._version import __version__

__all__ = ["__version__"]
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__ = "2.8.0"
60 changes: 60 additions & 0 deletions python/cuda_cccl/cuda/cccl/include_paths.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED.
#
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

from dataclasses import dataclass
from functools import lru_cache
import os
import shutil
from typing import Optional


def _get_cuda_path() -> Optional[str]:
cuda_path = os.environ.get("CUDA_PATH")
if cuda_path and os.path.exists(cuda_path):
return cuda_path

nvcc_path = shutil.which("nvcc")
if nvcc_path is not None:
return os.path.dirname(os.path.dirname(nvcc_path))

default_path = "/usr/local/cuda"
if os.path.exists(default_path):
return default_path

return None


@dataclass
class IncludePaths:
cuda: Optional[str]
libcudacxx: Optional[str]
cub: Optional[str]
thrust: Optional[str]

def as_tuple(self):
# Note: higher-level ... lower-level order:
return (self.thrust, self.cub, self.libcudacxx, self.cuda)


@lru_cache()
def get_include_paths() -> IncludePaths:
# TODO: once docs env supports Python >= 3.9, we
# can move this to a module-level import.
from importlib.resources import as_file, files

cuda_incl = None
cuda_path = _get_cuda_path()
if cuda_path is not None:
cuda_incl = os.path.join(cuda_path, "include")

with as_file(files("cuda.cccl.include")) as f:
cccl_incl = str(f)
assert os.path.exists(cccl_incl)

return IncludePaths(
cuda=cuda_incl,
libcudacxx=os.path.join(cccl_incl, "libcudacxx"),
cub=cccl_incl,
thrust=cccl_incl,
)
42 changes: 42 additions & 0 deletions python/cuda_cccl/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED.
#
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

[build-system]
requires = [
"setuptools>=61.0.0",
"wheel",
"packaging",
]
build-backend = "setuptools.build_meta"

[project]
name = "cuda-cccl"
description = "Experimental Package with CCCL headers to support JIT compilation"
authors = [
{ name = "NVIDIA Corporation" },
]
license = { file = "LICENSE" }
classifiers = [
"Programming Language :: Python :: 3 :: Only",
"Environment :: GPU :: NVIDIA CUDA",
"License :: OSI Approved :: Apache Software License",
]
requires-python = ">=3.9"
dynamic = ["version", "readme"]

[project.urls]
Homepage = "https://github.com/NVIDIA/cccl"
Documentation = "https://github.com/NVIDIA/cccl/tree/main/python/cuda_cccl"
Source = "https://github.com/NVIDIA/cccl/tree/main/python/cuda_cccl"
Tracker = "https://github.com/NVIDIA/cccl/issues"

[tool.setuptools.dynamic]
version = { attr = "cuda.cccl._version.__version__" }
readme = { file = ["README.md"], content-type = "text/markdown" }

[tool.setuptools.package-data]
"cuda" = ["cccl/include/**/*"]

[tool.setuptools.exclude-package-data]
"cuda" = ["cccl/include/__init__.py"]
40 changes: 40 additions & 0 deletions python/cuda_cccl/setup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED.
#
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

from setuptools import setup, find_namespace_packages
import os
import shutil

PROJECT_PATH = os.path.abspath(os.path.dirname(__file__))
CCCL_PATH = os.path.abspath(os.path.join(PROJECT_PATH, "..", ".."))


def copy_license():
src = os.path.abspath(os.path.join(CCCL_PATH, "LICENSE"))
dst = os.path.join(PROJECT_PATH, "LICENSE")
shutil.copy(src, dst)


def copy_cccl_headers_to_cuda_cccl_include():
cccl_headers = [["cub", "cub"], ["libcudacxx", "include"], ["thrust", "thrust"]]
inc_path = os.path.join(PROJECT_PATH, "cuda", "cccl", "include")
os.makedirs(inc_path, exist_ok=True)
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:
f.write("# Intentionally empty.\n")


copy_license()
copy_cccl_headers_to_cuda_cccl_include()

setup(
packages=find_namespace_packages(include=["cuda.*"]),
include_package_data=True,
)
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
1 change: 0 additions & 1 deletion python/cuda_cooperative/MANIFEST.in

This file was deleted.

45 changes: 8 additions & 37 deletions python/cuda_cooperative/cuda/cooperative/experimental/_nvrtc.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,9 @@
#
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

import os
import shutil
from cuda.bindings import nvrtc
from cuda.cooperative.experimental._caching import disk_cache
from cuda.cooperative.experimental._common import check_in, version
import importlib.resources as pkg_resources
import functools


Expand All @@ -19,22 +16,6 @@ def CHECK_NVRTC(err, prog):
raise RuntimeError(f"NVRTC error: {log.decode('ascii')}")


def get_cuda_path():
cuda_path = os.environ.get("CUDA_PATH", "")
if os.path.exists(cuda_path):
return cuda_path

nvcc_path = shutil.which("nvcc")
if nvcc_path is not None:
return os.path.dirname(os.path.dirname(nvcc_path))

default_path = "/usr/local/cuda"
if os.path.exists(default_path):
return default_path

return None


# cpp is the C++ source code
# cc = 800 for Ampere, 900 Hopper, etc
# rdc is true or false
Expand All @@ -46,24 +27,14 @@ def compile_impl(cpp, cc, rdc, code, nvrtc_path, nvrtc_version):
check_in("rdc", rdc, [True, False])
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")
cuda_include_path = os.path.join(get_cuda_path(), "include")

opts = [
b"--std=c++17",
bytes(f"--include-path={cub_path}", encoding="ascii"),
bytes(f"--include-path={thrust_path}", encoding="ascii"),
bytes(f"--include-path={libcudacxx_path}", encoding="ascii"),
bytes(f"--include-path={cuda_include_path}", encoding="ascii"),
bytes(f"--gpu-architecture=compute_{cc}", encoding="ascii"),
]
opts = [b"--std=c++17"]

from cuda.cccl.include_paths import get_include_paths

for path in get_include_paths().as_tuple():
if path:
opts += [f"--include-path={path}".encode("ascii")]
opts += [f"--gpu-architecture=compute_{cc}".encode("ascii")]
if rdc:
opts += [b"--relocatable-device-code=true"]

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.

Loading
Loading