Split cuda-cccl into headers, compute, and metapackage wheels - #10622
Split cuda-cccl into headers, compute, and metapackage wheels#10622tpn wants to merge 17 commits into
Conversation
|
Auto-sync is disabled for draft pull requests in this repository. Workflows must be run manually. Contributors can view more details about this message here. |
There was a problem hiding this comment.
Pull request overview
Splits the monolithic cuda-cccl Python distribution into a coordinated set of wheels (cccl-headers, cuda-compute, and a cuda-cccl metapackage) while preserving the public import surface (cuda.cccl.headers, cuda.cccl convenience exports, and cuda.compute) and updating CI, docs, and CMake packaging accordingly.
Changes:
- Introduces
cccl-headers(header payload + CMake packages) and refactorscuda-computeto depend on it (plus new version/serialization tagging). - Converts
cuda-ccclinto a metapackage and updates wheel build/test/release workflows to validate coordinated wheel sets and ownership. - Updates CUDAX CMake targets to use a non-imported implementation target wrapped by a global imported target to avoid system-include precedence issues, with corresponding CMake tests.
Reviewed changes
Copilot reviewed 113 out of 295 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
| test/cmake/test_export/CMakeLists.txt | Adds assertions for CUDAX target wrapping, compile features, and compile definitions. |
| python/cuda_compute/tests/compute/test_merge_sort.py | Updates package naming in NumPy-version comment. |
| python/cuda_compute/tests/compute/test_hostjit_paths.py | Adds tests ensuring HostJIT path env vars resolve to compute-owned bundled resources and preserve explicit env vars. |
| python/cuda_compute/tests/compute/test_deferred_annotations.py | Adds coverage for deferred annotations / future-annotations usage with gpu_struct and iterators. |
| python/cuda_compute/tests/compute/examples/unique/unique_by_key_basic.py | Adds unique_by_key basic example. |
| python/cuda_compute/tests/compute/examples/unique/init.py | Adds examples package marker for unique examples. |
| python/cuda_compute/tests/compute/examples/transform/unary_transform_object.py | Adds unary transform object-API example. |
| python/cuda_compute/tests/compute/examples/transform/unary_transform_basic.py | Adds unary transform basic example. |
| python/cuda_compute/tests/compute/examples/transform/binary_transform_object.py | Adds binary transform object-API example. |
| python/cuda_compute/tests/compute/examples/transform/binary_transform_basic.py | Adds binary transform basic example. |
| python/cuda_compute/tests/compute/examples/struct/struct_transform.py | Adds transform example using custom gpu_struct types. |
| python/cuda_compute/tests/compute/examples/struct/struct_reduction.py | Adds reduction example using custom gpu_struct types. |
| python/cuda_compute/tests/compute/examples/struct/init.py | Adds examples package marker for struct examples. |
| python/cuda_compute/tests/compute/examples/sort/segmented_sort_basic.py | Adds segmented sort example. |
| python/cuda_compute/tests/compute/examples/sort/radix_sort_object.py | Adds radix sort object-API example. |
| python/cuda_compute/tests/compute/examples/sort/radix_sort_buffer.py | Adds radix sort DoubleBuffer example. |
| python/cuda_compute/tests/compute/examples/sort/radix_sort_basic.py | Adds radix sort basic example. |
| python/cuda_compute/tests/compute/examples/sort/merge_sort_object.py | Adds merge sort object-API example. |
| python/cuda_compute/tests/compute/examples/sort/merge_sort_basic.py | Adds merge sort basic example. |
| python/cuda_compute/tests/compute/examples/sort/init.py | Adds examples package marker for sort examples. |
| python/cuda_compute/tests/compute/examples/serialization/ahead_of_time_compilation.py | Adds AOT compilation example with clean v2 skip behavior. |
| python/cuda_compute/tests/compute/examples/serialization/init.py | Adds examples package marker for serialization examples. |
| python/cuda_compute/tests/compute/examples/select/select_with_side_effect.py | Adds select example demonstrating side effects (atomic counter). |
| python/cuda_compute/tests/compute/examples/select/select_with_iterator.py | Adds select example using iterators. |
| python/cuda_compute/tests/compute/examples/select/select_basic.py | Adds select basic example. |
| python/cuda_compute/tests/compute/examples/select/init.py | Adds examples package marker for select examples. |
| python/cuda_compute/tests/compute/examples/segmented/segmented_reduce_object.py | Adds segmented reduce object-API example. |
| python/cuda_compute/tests/compute/examples/segmented/segmented_reduce_basic.py | Adds segmented reduce basic example. |
| python/cuda_compute/tests/compute/examples/segmented/init.py | Adds examples package marker for segmented examples. |
| python/cuda_compute/tests/compute/examples/scan/running_average.py | Adds inclusive-scan running-average example. |
| python/cuda_compute/tests/compute/examples/scan/inclusive_scan_object.py | Adds inclusive scan object-API example. |
| python/cuda_compute/tests/compute/examples/scan/inclusive_scan_custom.py | Adds inclusive scan custom-op example. |
| python/cuda_compute/tests/compute/examples/scan/exclusive_scan_object.py | Adds exclusive scan object-API example. |
| python/cuda_compute/tests/compute/examples/scan/exclusive_scan_max.py | Adds exclusive scan max-op example. |
| python/cuda_compute/tests/compute/examples/scan/init.py | Adds examples package marker for scan examples. |
| python/cuda_compute/tests/compute/examples/reduction/sum_reduction.py | Adds sum reduction example. |
| python/cuda_compute/tests/compute/examples/reduction/sum_reduction_lambda.py | Adds sum reduction lambda example. |
| python/cuda_compute/tests/compute/examples/reduction/sum_custom_reduction.py | Adds custom reduction example. |
| python/cuda_compute/tests/compute/examples/reduction/reduce_object.py | Adds reduction object-API example. |
| python/cuda_compute/tests/compute/examples/reduction/minmax_reduction.py | Adds min/max reduction example using gpu_struct. |
| python/cuda_compute/tests/compute/examples/reduction/min_reduction.py | Adds min reduction example. |
| python/cuda_compute/tests/compute/examples/reduction/init.py | Adds examples package marker for reduction examples. |
| python/cuda_compute/tests/compute/examples/raw_op/init.py | Adds examples package marker for raw-op examples. |
| python/cuda_compute/tests/compute/examples/partition/three_way_partition_basic.py | Adds three-way partition example. |
| python/cuda_compute/tests/compute/examples/partition/init.py | Adds examples package marker for partition examples. |
| python/cuda_compute/tests/compute/examples/iterator/zip_iterator_reduction.py | Adds zip-iterator reduction example. |
| python/cuda_compute/tests/compute/examples/iterator/zip_iterator_elementwise.py | Adds zip-iterator elementwise example. |
| python/cuda_compute/tests/compute/examples/iterator/zip_iterator_counting.py | Adds zip+counting iterator example. |
| python/cuda_compute/tests/compute/examples/iterator/transform_output_iterator.py | Adds transform-output-iterator example. |
| python/cuda_compute/tests/compute/examples/iterator/transform_iterator_lambda.py | Adds transform-iterator lambda example. |
| python/cuda_compute/tests/compute/examples/iterator/transform_iterator_basic.py | Adds transform-iterator basic example. |
| python/cuda_compute/tests/compute/examples/iterator/shuffle_iterator_basic.py | Adds shuffle-iterator example. |
| python/cuda_compute/tests/compute/examples/iterator/reverse_output_iterator.py | Adds reverse-output-iterator example. |
| python/cuda_compute/tests/compute/examples/iterator/reverse_input_iterator.py | Adds reverse-input-iterator example. |
| python/cuda_compute/tests/compute/examples/iterator/permutation_iterator_output.py | Adds permutation iterator output (scatter) example. |
| python/cuda_compute/tests/compute/examples/iterator/permutation_iterator_composed.py | Adds composed permutation+transform iterator example. |
| python/cuda_compute/tests/compute/examples/iterator/permutation_iterator_basic.py | Adds permutation iterator basic example. |
| python/cuda_compute/tests/compute/examples/iterator/discard_iterator_basic.py | Adds discard iterator example. |
| python/cuda_compute/tests/compute/examples/iterator/counting_iterator_basic.py | Adds counting iterator example. |
| python/cuda_compute/tests/compute/examples/iterator/constant_iterator_basic.py | Adds constant iterator example. |
| python/cuda_compute/tests/compute/examples/iterator/cache_modified_iterator_basic.py | Adds cache-modified iterator example. |
| python/cuda_compute/tests/compute/examples/iterator/init.py | Adds examples package marker for iterator examples. |
| python/cuda_compute/tests/compute/examples/histogram/histogram_even_basic.py | Adds histogram-even basic example. |
| python/cuda_compute/tests/compute/examples/histogram/init.py | Adds examples package marker for histogram examples. |
| python/cuda_compute/tests/compute/examples/free_threading/object_api.py | Adds free-threading object-API example. |
| python/cuda_compute/tests/compute/examples/free_threading/direct_api.py | Adds free-threading direct-API example. |
| python/cuda_compute/tests/compute/examples/free_threading/init.py | Adds examples package marker for free-threading examples. |
| python/cuda_compute/tests/compute/examples/binary_search/upper_bound_object.py | Adds upper_bound object-API example. |
| python/cuda_compute/tests/compute/examples/binary_search/upper_bound_basic.py | Adds upper_bound basic example. |
| python/cuda_compute/tests/compute/examples/binary_search/lower_bound_object.py | Adds lower_bound object-API example. |
| python/cuda_compute/tests/compute/examples/binary_search/lower_bound_basic.py | Adds lower_bound basic example. |
| python/cuda_compute/tests/compute/examples/binary_search/init.py | Adds examples package marker for binary-search examples. |
| python/cuda_compute/tests/_utils/device_array.py | Updates test utility docstring to cuda-compute. |
| python/cuda_compute/tests/_utils/init.py | Updates test utility package docstring to cuda-compute. |
| python/cuda_compute/cuda/compute/typing.py | Adds public typing helpers/protocols for device arrays/streams/operators. |
| python/cuda_compute/cuda/compute/op.py | Updates docs literalinclude path to new examples location. |
| python/cuda_compute/cuda/compute/iterators/_zip.py | Updates docs literalinclude path to new examples location. |
| python/cuda_compute/cuda/compute/iterators/_utils.py | Adds future-annotations helper module. |
| python/cuda_compute/cuda/compute/iterators/_transform.py | Updates docs literalinclude paths to new examples location. |
| python/cuda_compute/cuda/compute/iterators/_shuffle.py | Updates docs literalinclude path to new examples location. |
| python/cuda_compute/cuda/compute/iterators/_permutation.py | Updates docs literalinclude path to new examples location. |
| python/cuda_compute/cuda/compute/iterators/_counting.py | Updates docs literalinclude path to new examples location. |
| python/cuda_compute/cuda/compute/iterators/_constant.py | Updates docs literalinclude path to new examples location. |
| python/cuda_compute/cuda/compute/iterators/_common.py | Adds iterator common utilities (CUDA preamble + ensure_iterator). |
| python/cuda_compute/cuda/compute/iterators/init.py | Adds iterator public exports for cuda.compute.iterators. |
| python/cuda_compute/cuda/compute/determinism.py | Adds Determinism re-export module. |
| python/cuda_compute/cuda/compute/cccl/.gitkeep | Preserves package directory structure. |
| python/cuda_compute/cuda/compute/algorithms/_unique_by_key.py | Updates docs literalinclude paths to new examples location. |
| python/cuda_compute/cuda/compute/algorithms/_transform.py | Updates docs literalinclude paths to new examples location. |
| python/cuda_compute/cuda/compute/algorithms/_three_way_partition.py | Updates docs literalinclude paths to new examples location. |
| python/cuda_compute/cuda/compute/algorithms/_sort/_sort_common.py | Adds shared sort utilities (SortOrder/DoubleBuffer/_get_arrays). |
| python/cuda_compute/cuda/compute/algorithms/_sort/_segmented_sort.py | Hooks segmented sort into shared sort utilities and updated examples paths. |
| python/cuda_compute/cuda/compute/algorithms/_sort/_radix_sort.py | Hooks radix sort into shared sort utilities and updated examples paths. |
| python/cuda_compute/cuda/compute/algorithms/_sort/_merge_sort.py | Updates docs literalinclude path to new examples location. |
| python/cuda_compute/cuda/compute/algorithms/_sort/init.py | Adds public sort module exports. |
| python/cuda_compute/cuda/compute/algorithms/_select.py | Updates docs literalinclude paths to new examples location. |
| python/cuda_compute/cuda/compute/algorithms/_segmented_reduce.py | Updates docs literalinclude paths to new examples location. |
| python/cuda_compute/cuda/compute/algorithms/_scan.py | Updates docs literalinclude paths to new examples location. |
| python/cuda_compute/cuda/compute/algorithms/_reduce.py | Updates docs literalinclude paths to new examples location. |
| python/cuda_compute/cuda/compute/algorithms/_histogram.py | Updates docs literalinclude paths to new examples location. |
| python/cuda_compute/cuda/compute/algorithms/_binary_search.py | Updates docs literalinclude paths to new examples location. |
| python/cuda_compute/cuda/compute/_version.py | Adds cuda-compute distribution version lookup for runtime/serialization tagging. |
| python/cuda_compute/cuda/compute/_utils/init.py | Adds identifier sanitization helper for JIT symbol naming. |
| python/cuda_compute/cuda/compute/_target_cc.py | Adds context-local target-cc plumbing for multi-arch builds. |
| python/cuda_compute/cuda/compute/_serialization/dispatch.py | Adds public serialize/deserialize entry points and concurrency warning. |
| python/cuda_compute/cuda/compute/_serialization/codec.py | Switches serialization version stamping/messages to cuda-compute. |
| python/cuda_compute/cuda/compute/_serialization/init.py | Adds serialization public exports. |
| python/cuda_compute/cuda/compute/_nvtx.py | (Touched) NVTX integration placeholder/module. |
| python/cuda_compute/cuda/compute/_device_code.py | Adds DeviceCode wrapper type for op code blobs. |
| python/cuda_compute/cuda/compute/_cuda_version_utils.py | Improves missing cuda.bindings error message to reference cuda-compute extras. |
| python/cuda_compute/cuda/compute/_cpp_compile.py | Switches include-path discovery to cuda.cccl.headers. |
| python/cuda_compute/cuda/compute/_cccl_interop.py | Switches include-path discovery to cuda.cccl.headers. |
| python/cuda_compute/cuda/compute/_bindings_segmented_reduce_backend_v2.pxi | Adds HostJIT (v2) segmented-reduce call helper shim. |
| python/cuda_compute/cuda/compute/_bindings_segmented_reduce_backend_v1.pxi | Adds v1 segmented-reduce call helper shim. |
| python/cuda_compute/cuda/compute/_bindings_op_code_type_v2.pxi | Adds v2 op-code-type enum parsing including LLVM IR. |
| python/cuda_compute/cuda/compute/_bindings_op_code_type_v1.pxi | Adds v1 op-code-type enum parsing. |
| python/cuda_compute/cuda/compute/_bindings_binary_search_backend_v2.pxi | Adds v2 binary-search build-result helper. |
| python/cuda_compute/cuda/compute/_bindings_binary_search_backend_v1.pxi | Adds v1 binary-search build-result helper. |
| python/cuda_compute/cuda/compute/init.py | Adds __version__ export and updates HostJIT bundle path logic to compute-owned _hostjit. |
| python/cuda_compute/benchmarks/compute/transform/fill.py | Adds Python benchmark for fill via ConstantIterator + unary transform. |
| python/cuda_compute/benchmarks/compute/README.md | Updates benchmark docs for cuda-compute / cccl-headers split. |
| python/cuda_compute/benchmarks/compute/pixi.toml | Updates pixi environments/deps to install cuda-compute and local cccl-headers. |
| python/cuda_compute/benchmarks/compute/AGENTS.md | Updates contributor guidance paths for split package layout. |
| python/cuda_compute/benchmarks/compute/.gitignore | Adds ignores for benchmark build/results/pixi artifacts. |
| python/cuda_compute/.gitignore | Adds standard Python/CMake ignore patterns for the new project layout. |
| python/cuda_cccl/tests/test_cuda_cccl_metadata.py | Adds tests asserting the metapackage has no Python payload and forwards extras/version-pinned deps. |
| python/cuda_cccl/tests/headers/test_cuda_cccl.py | Removes old header tests from metapackage tree (now owned by cccl-headers). |
| python/cuda_cccl/LICENSE | Removes redundant LICENSE pointer file from metapackage project. |
| python/cuda_cccl/cuda/cccl/parallel/experimental/init.py | Removes deprecated cuda.cccl.parallel.experimental alias module. |
| python/cuda_cccl/cuda/cccl/parallel/init.py | Removes cuda.cccl.parallel package wrapper. |
| python/cuda_cccl/cuda/cccl/headers/include/init.py | Removes old header payload marker from metapackage tree. |
| python/cuda_cccl/cuda/cccl/headers/include_paths.py | Removes old include-path helper (migrated to cccl-headers). |
| python/cuda_cccl/cuda/cccl/headers/init.py | Removes old headers package init (migrated to cccl-headers). |
| python/cuda_cccl/cuda/cccl/init.py | Removes old cuda.cccl implementation from metapackage tree. |
| python/cuda_cccl/.gitignore | Simplifies ignore list for metapackage project. |
| python/cccl_headers/README.md | Adds cccl-headers package overview and usage snippet. |
| python/cccl_headers/cuda/cccl/py.typed | Marks cuda.cccl typing support for cccl-headers. |
| python/cccl_headers/cuda/cccl/headers/include/init.py | Adds header payload root marker for bundled headers. |
| python/cccl_headers/cuda/cccl/headers/include_paths.py | Adds include-path discovery implementation using cuda-pathfinder. |
| python/cccl_headers/cuda/cccl/headers/init.py | Adds cccl-headers public API (IncludePaths, get_include_paths, __version__). |
| python/cccl_headers/cuda/cccl/init.py | Adds convenience exports from cuda.cccl for header distribution. |
| python/cccl_headers/CMakeLists.txt | Adds language-free install-only CMake project for packaging headers/CMake configs/licenses. |
| lib/cmake/cudax/cudax-config.cmake | Reworks CUDAX target to wrap non-imported implementation in imported global target; adds CUDA feature enablement when CUDA language enabled. |
| lib/cmake/cccl/cccl-config.cmake | Simplifies CUDAX aliasing to point at the public wrapper target. |
| docs/python/resources.rst | Updates examples link and setup guidance for split wheel installs. |
| docs/python/index.rst | Documents split distributions and adds headers API entry point. |
| docs/python/headers_api.rst | Adds autodoc page for cuda.cccl.headers. |
| docs/python/compute/developer_overview.rst | Updates source-tree path references for compute + headers packages. |
| docs/python/api_reference.rst | Adds headers API to docs toctree. |
| docs/conf.py | Updates autodoc sys.path setup to include both cuda_compute and cccl_headers trees. |
| ci/windows/test_cuda_compute_python.ps1 | Updates Windows compute lane to install coordinated wheelhouse and validate no implicit metapackage install. |
| ci/windows/test_cuda_compute_minimal_python.ps1 | Updates Windows minimal compute lane to install headers + compute from wheelhouse and run tests. |
| ci/windows/test_cuda_cccl_headers_python.ps1 | Updates Windows headers lane to install only cccl-headers and run its tests. |
| ci/windows/test_cuda_cccl_examples_python.ps1 | Updates examples lane to install coordinated wheels and run compute examples + benchmark smoke test. |
| ci/windows/build_common_python.psm1 | Refactors wheelhouse helpers to locate coordinated wheels (headers/compute/meta). |
| ci/util/workflow/get_wheel_artifact_name.sh | Fixes missing echo and treats null job fields as missing. |
| ci/update_version.sh | Removes update hook for deleted monolithic cuda.cccl version file. |
| ci/test/python_wheels/CMakeLists.txt | Adds CI test to run wheel artifact collection/validation scripts. |
| ci/test/inspect_changes/python_cuda_compute.output | Adds inspect_changes golden output for cuda_compute project. |
| ci/test/inspect_changes/python_cuda_compute.dirty_files | Adds inspect_changes dirty-files list for cuda_compute project. |
| ci/test/inspect_changes/python_cccl_headers.output | Adds inspect_changes golden output for cccl_headers project. |
| ci/test/inspect_changes/python_cccl_headers.dirty_files | Adds inspect_changes dirty-files list for cccl_headers project. |
| ci/test/CMakeLists.txt | Registers new python_wheels CI test subdirectory. |
| ci/test_python_common.sh | Updates local CI install constraints to include all three coordinated projects. |
| ci/test_cuda_compute_minimal_python.sh | Updates Linux minimal compute lane to install headers + compute from wheelhouse and run tests. |
| ci/test_cuda_compute_minimal_python_tsan.sh | Updates TSan lane to install headers + compute and run free-threaded tests under TSan. |
| ci/test_cuda_cccl_headers_python.sh | Updates Linux headers lane to install only cccl-headers and run its tests from outside the source tree. |
| ci/project_files_and_dependencies.yaml | Updates Python project dependency graph and include regexes to cover split projects and CUDAX public changes. |
| ci/matrix.yaml | Renames Python wheel build job and updates examples job naming. |
| ci/generate_version.sh | Adds strict bash flags and makes JSON path robust relative to CI directory. |
| ci/build_cuda_cccl_wheel.sh | Updates wheel build script to produce coordinated wheelhouse (universal headers/meta + platform compute) with reproducibility support. |
| ci/build_cuda_cccl_python_v2.sh | Updates wrapper comment for v2 HostJIT build to reflect new compute wheel target. |
| ci/build_cuda_cccl_python_tsan.sh | Updates wrapper comment for TSan build to reflect new compute wheel target. |
| ci/bench/README.md | Updates benchmark infra docs to handle pre/post split layouts. |
| c/parallel.v2/src/hostjit/libnvcc/CMakeLists.txt | Gates embedding build-tree HostJIT paths behind a new runtime-paths option. |
| c/parallel.v2/CMakeLists.txt | Adds option to use runtime HostJIT resource paths rather than embedded build-tree defaults. |
| .pre-commit-config.yaml | Splits mypy into separate hooks for cuda.compute and cuda.cccl.headers projects. |
| .github/workflows/release-wheels.yml | Tightens release workflow: validates trusted wheel run, collects coordinated wheels, validates set before publish. |
| .github/workflows/ci-workflow-pull-request.yml | Updates CI smoke install to install coordinated wheels (headers/compute/meta) and run pip check. |
| .coderabbit.yaml | Broadens review focus instructions to cover all Python paths. |
There was a problem hiding this comment.
Actionable comments posted: 1
Note
Due to the large number of review comments, Critical severity comments were prioritized as inline comments.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
docs/python/setup.rst (1)
25-30: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winimportant: Correct the CUDA Toolkit prerequisite.
Lines 25-30 require a preinstalled Toolkit. Lines 45-46 state that
cu12andcu13installcuda-toolkit. State that users need either acu*extra or a compatible system Toolkit. Reserve the system Toolkit prerequisite forsysctk*variants.ci/windows/build_cuda_cccl_python.ps1 (1)
255-272: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick winimportant: forward the coordinated version and
SOURCE_DATE_EPOCHinto the nested CUDA 13 container.Lines 83-114 state the intent that the outer build, the nested CUDA 13 build, and the universal wheel builds share one version. The nested invocation does not pass
SETUPTOOLS_SCM_PRETEND_VERSION_FOR_CUDA_COMPUTEorSOURCE_DATE_EPOCH. The nested script therefore re-derives both from git inside the CUDA 13 image. Any difference in git state, tag visibility, orsafe.directoryhandling in that image produces a different compute wheel version, and the merge step then combines mismatched versions. Forwarding the values also removes the redundant unshallow and version generation in the nested container.Proposed fix
'--env', "CCCL_PYTHON_USE_V2=$($env:CCCL_PYTHON_USE_V2)", + '--env', "SETUPTOOLS_SCM_PRETEND_VERSION_FOR_CUDA_COMPUTE=$($env:SETUPTOOLS_SCM_PRETEND_VERSION_FOR_CUDA_COMPUTE)", + '--env', "SOURCE_DATE_EPOCH=$($env:SOURCE_DATE_EPOCH)", $Cuda13Image,
🟠 Major comments (30)
.github/workflows/release-wheels.yml-67-82 (1)
67-82: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick winMissing Branch Authorization (CWE-862): Missing Authorization
Reachability: Internal · Exploitability: Moderate
important: Restrict accepted runs to approved release branches. The workflow currently accepts successful
workflow_dispatchruns from any branch, so a feature-branch build can reach the PyPI publish step. Add an allowlist for.head_branch, such asmainand approvedbranch/{major}.{minor}.xbranches.ci/test/python_wheels/test_wheel_scripts.py-512-525 (1)
512-525: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick winimportant: the post-uninstall isolation check depends on undefined behavior and can fail for the wrong reason.
importlib.util.find_spec('cuda.compute')imports the parent packagecudafirst. The old metapackage is the only distribution that providescuda/, so after the uninstall two outcomes are possible:
- pip removes the now-empty directories.
import cudathen fails andfind_specraisesModuleNotFoundErrorinstead of returningNone.check=Truereports a test failure.- pip leaves the empty
cuda/compute/directory. That directory resolves as a namespace package, sofind_specreturns a spec and the assertion fails.pip does not guarantee either outcome, so this assertion does not reliably test what it intends. Assert that no importable module with a loader remains, and treat a missing parent as success.
Proposed fix
- ( - "import importlib.util; " - "assert importlib.util.find_spec('cuda.compute') is None; " - "assert importlib.util.find_spec('cuda.cccl') is None" - ), + ( + "import importlib.util\n" + "def gone(name):\n" + " try:\n" + " spec = importlib.util.find_spec(name)\n" + " except ModuleNotFoundError:\n" + " return True\n" + " return spec is None or spec.loader is None\n" + "assert gone('cuda.compute')\n" + "assert gone('cuda.cccl')\n" + ),python/cuda_compute/benchmarks/compute/scan/exclusive/sum.py-82-82 (1)
82-82: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick winimportant: Limit the default element-count axis.
The exponent
32creates2^32elements. Input and output alone require 32 GiB for 32-bit types and 64 GiB for 64-bit types. Temporary storage increases this requirement.Cap the default axis or skip cases that exceed available GPU memory. Otherwise, a normal unfiltered run can terminate before it records the smaller cases.
As per path instructions, benchmark changes must avoid excessive runtime and preserve useful comparison data.
Source: Path instructions
python/cuda_compute/benchmarks/compute/utils.py-53-67 (1)
53-67: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick winimportant: Keep
float64interpolation finite.For
np.float64,max_val - min_valoverflows toinf. Production probabilities then produceinfornan, causing incorrect select thresholds. Use(1.0 - probability) * min_val + probability * max_val.Source: Path instructions
python/cuda_compute/tests/compute/test_no_numba.py-268-270 (1)
268-270: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick winimportant:
cuda-computedeclaresnumpywithout a lower bound, so NumPy 1.x remains supported. Usenp.argsort(h_keys, kind="stable");stable=TrueraisesTypeErroron NumPy 1.x.python/cuda_compute/tests/compute/test_radix_sort.py-322-324 (1)
322-324: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick winimportant: both bit-window tests use
dtype().itemsizeas a bit count.itemsizeis a byte count, so the begin/end bit sets stay within 0..8 and high key bits are never sorted.host_sortrepeats the same bounds, so the assertions pass and the coverage loss is silent.
python/cuda_compute/tests/compute/test_radix_sort.py#L322-L324: changenum_bits = dtype().itemsizetodtype().itemsize * 8intest_radix_sort_pairs_bit_window.python/cuda_compute/tests/compute/test_radix_sort.py#L377-L379: apply the same change intest_radix_sort_pairs_double_buffer_bit_window.python/cuda_compute/tests/test_examples.py-119-127 (1)
119-127: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick winimportant:
subprocess.runhas no timeout. If an example hangs, the whole test session hangs until the CI job is killed. Add a timeout and report it as a failure.result = subprocess.run( [sys.executable, module_file], capture_output=True, text=True, cwd=os.path.dirname(module_file), + timeout=600, )python/cccl_headers/cuda/cccl/headers/include_paths.py-16-23 (1)
16-23: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick winimportant: Make the cached
IncludePathsvalue immutable.
get_include_pathsreturns the same mutable object for each cachedprobe_file. One caller can modify a field and corrupt the include paths returned to later callers.Declare the dataclass with
@dataclass(frozen=True), or cache immutable path values and construct a new result for each call.Also applies to: 50-64
Source: Path instructions
python/cuda_compute/cuda/compute/_bindings_impl.pyx-236-236 (1)
236-236: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick winimportant: Add
except NULLtoget_buffer_pointerWhen
PyObject_GetBufferfails, callers receiveNULLwithout the Python exception being propagated.ctypes_typed_pointer_payload_ptrcan then dereferenceptr_ref[0]and crash.python/cuda_compute/cuda/compute/_jit.py-826-837 (1)
826-837: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick winimportant: the
tryblock catches onlyOSErrorandTypeError. Two other failures are reachable here.ast.parseraisesSyntaxErrorwhen the retrieved source is not a standalone statement, which happens for a lambda:inspect.getsourcereturns the enclosing call expression, andfunc.__name__is<lambda>, so Line 866 would also raiseKeyError.to_jit_op_adapterroutes any callable with captured device arrays through this function, so a lambda that closes over a device array reaches it. Wrap parse and lookup failures in the same actionableValueError, or reject non-defcallables with an explicit message.python/cuda_compute/cuda/compute/_jit.py-668-674 (1)
668-674: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick winimportant: both
OpAdaptersubclasses implement__eq__by dereferencing attributes onotherwith no type check. Python calls__eq__on hash collisions inside dicts and sets, and both classes are registered as cache-key providers at Line 1046 and Line 1048. A collision with any foreign key type raisesAttributeErrorinstead of returningNotImplemented.
python/cuda_compute/cuda/compute/_jit.py#L668-L674: guard_StatelessOp.__eq__withisinstance(other, _StatelessOp)and returnNotImplementedotherwise.python/cuda_compute/cuda/compute/_jit.py#L1027-L1031: guard_StatefulOp.__eq__withisinstance(other, _StatefulOp)and returnNotImplementedotherwise.python/cuda_compute/tests/compute/test_three_way_partition.py-48-50 (1)
48-50: 🚀 Performance & Scalability | 🟠 Major | ⚡ Quick winimportant: this builds 77 parameterizations, and
log_size16 and 20 run 65K and 1M element partitions for all 11 dtypes. Each case compiles two custom operators. The siblingtest_unique_by_key.pymarks sizes at or above2**20withpytest.mark.largethroughget_mark. Apply the same marking here so a default CI run does not pay for 11 million-element partitions.Source: Path instructions
python/cuda_compute/tests/compute/test_unique_by_key.py-143-144 (1)
143-144: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick winimportant: both test files define their own
random_arrayhelper that acceptsmax_valuebut applies it only to integral dtypes. For every floating-point dtype the helper returns values in[0, 1), so the callers' value-based predicates and equality checks degenerate and the float parameterizations lose their coverage. Fix the helper in both places, or move one corrected helper intotests/_utils.
python/cuda_compute/tests/compute/test_unique_by_key.py#L143-L144: scale the float keys into a small integral range so consecutive duplicate keys occur and the de-duplication path runs.python/cuda_compute/tests/compute/test_three_way_partition.py#L87-L93: scale the float inputs across the42threshold so the float cases produce all three partitions.python/cuda_compute/benchmarks/compute/.gitignore-17-19 (1)
17-19: 📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick winimportant: ignoring
pixi.lockleaves the benchmark environment unpinned. A benchmark run on one machine can then resolve different dependency versions than another, which invalidates cross-run performance comparison. The benchmarking conventions require reproducible runs and comparable results. Commitpixi.lockand ignore only.pixi/.# Pixi .pixi/ -pixi.lockAs per path instructions: "Check that benchmark changes measure meaningful workloads, keep axes comparable, avoid excessive runtime, and preserve useful comparison data."
Source: Path instructions
python/cccl_headers/tests/test_cccl_headers.py-249-252 (1)
249-252: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick winimportant: Line 250, Line 251, Line 293, Line 294, and Line 295 pass CMake cache values through
.as_posix(), but Line 249 and Line 252 interpolate the rawPath. On Windowsstr(Path)produces backslashes, and CMake treats a backslash in a cache value as an escape. The result is a brokencudax_DIRandCMAKE_INSTALL_PREFIX. Use.as_posix()for both.- f"-Dcudax_DIR={cudax_cmake_dir}", + f"-Dcudax_DIR={cudax_cmake_dir.as_posix()}", f"-DCCCL_DIR={(package_root / 'lib' / 'cmake' / 'cccl').as_posix()}", f"-DEXPECTED_CUDAX_INCLUDE={(package_root / 'include').as_posix()}", - f"-DCMAKE_INSTALL_PREFIX={build_dir / 'install'}", + f"-DCMAKE_INSTALL_PREFIX={(build_dir / 'install').as_posix()}",python/cuda_compute/cuda/compute/struct.py-97-107 (1)
97-107: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick winimportant: Do not discard constructor arguments. Positional arguments cause all keyword arguments to be ignored. A dictionary first argument also causes trailing positional arguments to be ignored. Reject unsupported mixes, or merge them with duplicate and missing-field validation.
python/cuda_compute/cuda/compute/struct.py-146-163 (1)
146-163: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick winimportant: Add coercion for declared
TypeDescriptorfields._field_spec_to_type_descriptorsacceptstypes.TypeDescriptor, but_coerce_valuehas no matching branch.gpu_struct({"x": types.int32})(1)reaches the fallback and raisesAttributeError. Validate nested struct classes before accepting them, and coerce descriptors throughfield_type.dtype.type(...).python/cuda_compute/cuda/compute/types.py-74-80 (1)
74-80: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick winimportant: Freeze the field mapping at construction.
StructTypeDescriptorstores and exposes the caller’s mutable dictionary, while equality, hashing, andlayout_key()read it. A later mutation changes cache identity while_dtyperemains unchanged. Copy the mapping and expose an immutable view.Also applies to: 90-108
python/cuda_compute/cuda/compute/types.py-260-265 (1)
260-265: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick winimportant: Collect every annotated positional parameter. The
breakleavesinput_tdswith only the first annotated parameter. A multi-argument operator then gets a truncated signature. Remove thebreakand define explicit handling for missing annotations.python/cuda_compute/cuda/compute/types.py-171-179 (1)
171-179: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick winimportant: Preserve structured-dtype layouts
from_numpy_dtype()andgpu_struct()rebuild structured dtypes from field types withalign=True. This discards source offsets anditemsize, so packed or explicitly padded dtypes can receive a different record ABI. Preserve the source layout, or reject unsupported layouts, in both paths.gpu_struct()must also handle title aliases without treating them as additional fields.ci/windows/test_cuda_compute_python.ps1-29-38 (1)
29-38: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick winimportant: Install only
"$wheelPath[test-$ctkFlavor$cudaMajor]"and let pip resolvecccl-headersfrom$wheelhouse. The current command installs$headersWheelPathseparately, so the import assertion cannot detect a missing dependency incuda-compute. Run this check in a fresh environment and assert the installedcccl-headersdistribution.python/cuda_compute/cuda/compute/iterators/_discard.py-70-84 (1)
70-84: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick winimportant: Do not expose the discard iterator as input-capable. This no-op operation does not write
result, butIteratorBasetreats its non-Nonereturn as a valid input dereference. An algorithm that receivesDiscardIteratorasd_inreads uninitialized device data. ReturnNonehere so input conversion fails.python/cuda_compute/cuda/compute/iterators/_reverse.py-37-41 (1)
37-41: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick winimportant: Define empty-array behavior before calculating
size - 1. For an empty device array, this creates aPointerIteratorat-1elements and serializes a pointer before the allocation. Reject the input or use a valid empty-range representation.python/cuda_compute/cuda/compute/iterators/_reverse.py-139-140 (1)
139-140: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick winimportant: Negate the host-side offset. The class contract and
_make_advance_op()move positive offsets backward. This method moves the wrapped iterator forward instead. For an array-backed iterator,ReverseIterator(array) + 1points one element past the end instead of at the preceding element.python/cuda_compute/cuda/compute/iterators/_base.py-76-81 (1)
76-81: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick winimportant: Resolve the implicit iterator cache key to the current compute capability. When
USING_V2is false,get_target_cc()returnsNonefor default builds, and all three accessors useNoneas the cache key. Switching devices can reuse the first device's incompatible LTO-IR.python/cuda_compute/cuda/compute/iterators/_cache_modified.py-69-78 (1)
69-78: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick winimportant: Reject descriptors without a generated C++ type. A 4-byte structured dtype passes the size check, but
cpp_type_from_descriptor()returnsNone, producingstatic_cast<None**>and a JIT compilation failure.python/cuda_compute/cuda/compute/iterators/_cache_modified.py-96-100 (1)
96-100: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick winimportant: Use
int64_tfor the advance distance.ReverseIteratorpasses a negativeint64_tto the wrapped iterator. Reading it asuint64_tconverts-nto2^64 - n, causing an invalid positive pointer adjustment.python/cuda_compute/cuda/compute/_utils/temp_storage_buffer.py-20-40 (1)
20-40: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick winimportant: Bind memory-pool configuration and default-stream cleanup to the allocation device. When
streamtargets another device,_set_default_mempool_threshold(dev.device_id)configures the wrong pool. WhenstreamisNone, finalization can enqueuecudaFreeAsyncon another thread's default device and leave the allocation reserved after the warning. Store the allocation device and preserve the allocation stream or device for cleanup. Add multi-GPU tests for cross-device streams and cross-thread finalization.ci/windows/test_cuda_compute_minimal_python.ps1-29-37 (1)
29-37: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick winimportant: Do not pass
$headersWheelPathas a direct install target. The command installscccl-headerseven if thecuda-computewheel omits its required dependency. Becausepip checkvalidates declared installed dependencies, it cannot detect that omission. Install only"$wheelPath[minimal-$ctkFlavor$cudaMajor]"with--find-links $wheelhouse, then assert that the resolver installed the matchingcccl-headersdistribution. (pip.pypa.io)Based on PR objectives, the split requires dependency validation across the wheel set.
python/cuda_compute/README.md-17-19 (1)
17-19: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick winimportant: Add the required cleanup and reinstall procedure to the installation section. The README directs existing aggregate users to continue installing
cuda-ccclbut does not provide the required migration from the monolithic wheel. Users can otherwise retain files from the old wheel-ownership layout. As per PR objectives, the split requires an uninstall/reinstall procedure when users upgrade from the monolithic wheel.
🟡 Minor comments (21)
docs/python/compute/index.rst-311-311 (1)
311-311: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winimportant: Quote the package extras in these installation commands.
Unquoted square brackets can be interpreted as pathname patterns by the shell. Use quoted requirements such as
pip install 'cuda-compute[minimal-cu13]'andpip install 'cuda-compute[minimal-sysctk13]'.Also applies to: 541-542
Source: Path instructions
ci/util/python/validate_cccl_wheel_set.py-71-75 (1)
71-75: 🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick winimportant: Parse extra markers instead of matching one rendered form. A valid marker such as
(python_version < "3.11") and extra == "test"or a changed version bound is classified as unconditional, which can block wheel validation. Use a complete marker parser and add regression tests for these forms.python/cuda_compute/benchmarks/compute/partition/three_way.py-67-68 (1)
67-68: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick winimportant: Report both counter writes.
d_num_selected_outstores two counters, but the accounting reports one. Use2 * d_num_selected_out.dtype.itemsizeto match C++add_global_memory_writes<offset_t>(2).np.int32is a supported output dtype and does not cause an out-of-bounds write.python/cuda_compute/tests/compute/examples/sort/radix_sort_basic.py-45-47 (1)
45-47: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick winimportant: Replace
stable=Truewithkind="stable"in both sort examples.python/cuda_compute/pyproject.tomldeclaresnumpywithout a minimum version, and NumPy 1.x raisesTypeErrorforstable=True.python/cuda_compute/benchmarks/compute/segmented_reduce/variable_sum.py-118-118 (1)
118-118: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winimportant: Report both offset arrays in segmented-operation metrics.
Both benchmarks pass separate start and end offset arrays but report traffic for one contiguous
num_segments + 1array.
python/cuda_compute/benchmarks/compute/segmented_reduce/variable_sum.py#L118-L118: report2 * num_segments * start_offsets.dtype.itemsize.python/cuda_compute/benchmarks/compute/segmented_sort/keys.py#L126-L126: report2 * num_segments * start_offsets.dtype.itemsize.As per path instructions, benchmark results must preserve useful comparison data.
Source: Path instructions
python/cuda_compute/tests/compute/examples/select/select_object.py-57-78 (1)
57-78: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winimportant: Validate every demonstrated selection result.
Both examples execute and retrieve an output that the assertions do not validate.
python/cuda_compute/tests/compute/examples/select/select_object.py#L57-L78: assert the second selected count and values.python/cuda_compute/tests/compute/examples/select/select_with_side_effect.py#L39-L54: assert the selected even values, not only the counts.python/cuda_compute/tests/compute/examples/sort/radix_sort_buffer.py-21-23 (1)
21-23: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick winsuggestion:
8.3 - 1in the literal list looks like a typo for8.3, -1. The list still has 10 entries, so the example passes, but a reader of a public example will stop here. Replace it with the intended literal.h_in_values = np.array( - [-3.2, 2.2, 1.9, 4.0, -3.9, 2.7, 0, 8.3 - 1, 2.9, 5.4], dtype="float32" + [-3.2, 2.2, 1.9, 4.0, -3.9, 2.7, 0, 7.3, 2.9, 5.4], dtype="float32" )python/cuda_compute/benchmarks/compute/transform/complex_cmp.py-84-86 (1)
84-86: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winimportant: the write accounting is off by one element. The transform writes
num_items = num_elements - 1booleans, but line 86 chargesnum_elements. Usenum_itemsso the reported write bandwidth matches the C++ benchmark.state.add_element_count(num_elements) state.add_global_memory_reads(num_elements * d_in.dtype.itemsize) - state.add_global_memory_writes(num_elements * d_out.dtype.itemsize) + state.add_global_memory_writes(num_items * d_out.dtype.itemsize)Note
num_itemsis currently defined at line 79, after this block would need it; move the assignment abovestate.add_element_count.Source: Path instructions
python/cuda_compute/tests/compute/examples/sort/radix_sort_buffer.py-49-49 (1)
49-49: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick winimportant: Use
kind="stable"or require NumPy 2.0+.cuda-computedeclares an unconstrainednumpydependency, so NumPy 1.x remains possible;stable=TrueraisesTypeErrorthere.python/cuda_compute/tests/compute/test_reduce.py-473-487 (1)
473-487: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winimportant: This block builds
reducer_1andreducer_2for two identical-body ops with different names, then never asserts anything. The case is dead code and covers nothing.Add the expected assertion. Based on the neighbouring cases and the comment, ops with the same body but different names should map to the same cached reducer:
reducer_2 = cuda.compute.make_reduce_into( d_in=TransformIterator(CountingIterator(np.int32(0)), op3), d_out=DeviceArray.empty(1, dtype="int64"), op=sum_op, h_init=np.zeros(1, dtype="int64"), ) + assert reducer_1 is reducer_2If the intended behaviour is the opposite, use
is notinstead. Confirm the caching contract before choosing.python/cuda_compute/tests/compute/examples/raw_op/llvm_stateless.py-13-22 (1)
13-22: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick winimportant: Exclude
llvmlite 0.43.xfrom the example test dependencies or rewrite the IR with typed pointers.llvmlite 0.43.xdoes not support the opaqueptrsyntax used here. The example runner already handlessys.exit(0), andllvm.initialize()is not required by currentllvmlitereleases.python/cuda_compute/tests/compute/examples/scan/running_average.py-1-16 (1)
1-16: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick winimportant: Add the license header and move
# example-beginabove the imports.This file has no NVIDIA copyright/SPDX header. Every sibling example in
python/cuda_compute/tests/compute/examples/scan/carries one, and the repository guidelines require the license text in all source files.The
# example-beginmarker at Line 12 also sits after the imports. Documentation extraction betweenexample-beginandexample-endtherefore produces a snippet withoutimport cupy,import numpy, or thecuda.computeimports. The docstring at Lines 13-15 is likewise not a module docstring because statements precede it.As per coding guidelines: "All headers, and also source files, must have the correct license text."
+# Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +# example-begin +""" +Inclusive scan using zip iterator and output transform iterator to compute running average. +""" + import cupy as cp import numpy as np import cuda.compute from cuda.compute import ( ConstantIterator, TransformOutputIterator, ZipIterator, gpu_struct, ) - -# example-begin -""" -Inclusive scan using zip iterator and output transform iterator to compute running average. -"""Source: Coding guidelines
python/cuda_compute/tests/compute/test_iterators.py-107-118 (1)
107-118: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick winimportant:
test_reverse_output_iterator_equalityduplicates the input test.ReverseIteratorsupports both input and output dereference, but this test only compareskindand does not exercise output dereference. Delete the duplicate or replace it with an output-operation test.python/cuda_compute/cuda/compute/_jit.py-818-820 (1)
818-820: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick winsuggestion: the docstring says the state arrays appear "after the regular parameters".
_AddStateParametersprepends them, and the example at Line 812 showsdef func(state, x). Correct the docstring to say the state parameters come first.python/cuda_compute/.gitignore-30-31 (1)
30-31: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick winsuggestion: Replace
*.cmakewith explicit generated-file patterns such ascmake_install.cmakeandCTestTestfile.cmake. Remove the no-op!CMakeLists.txtrule.python/cuda_compute/cuda/compute/_utils/protocols.py-73-85 (1)
73-85: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick winimportant: Catch
TypeErroras well asAttributeError. PyTorch exposesTensor.sizeas a method, soint(arr.size)raisesTypeErrorbefore the shape fallback.python/cuda_compute/cuda/compute/struct.py-58-63 (1)
58-63: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winimportant: Reject Python keywords and names defined by or required by
_Struct._jit.pygenerates invalid attribute expressions for keyword fields, and_fieldsoverwrites the instance field map before record conversion.python/cuda_compute/cuda/compute/typing.py-64-64 (1)
64-64: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winimportant: Add
StreamLiketo__all__. The protocol is public and documented, but wildcard imports and API tools that honor__all__omit it.python/cuda_compute/cuda/compute/typing.py-61-61 (1)
61-61: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick winimportant: Close the
RawOpcross-reference target. The missing>prevents Sphinx from resolvingcuda.compute.op.RawOp.python/cuda_cccl/README.md-11-14 (1)
11-14: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winimportant: Quote both extras specifiers. In zsh, unquoted square brackets can cause
no matches foundbeforepipruns. Usepip install "cuda-cccl[cu13]"andpip install "cuda-cccl[cu12]".python/cuda_compute/README.md-22-23 (1)
22-23: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winimportant: Quote every requirement that contains an extra. Unquoted
[...]is shell glob syntax. A shell can alter or reject the requirement beforepipparses it. Use"cuda-compute[cu13]"and the same quoting for all variants, including the aggregate example. (pip.pypa.io)Also applies to: 30-31, 37-38, 42-42
🧹 Nitpick comments (31)
c/parallel.v2/src/hostjit/libnvcc/compiler.cpp (1)
135-141: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winimportant: Apply the required CCCL C++ naming and function declarations.
Rename
appendClangResourceDirtoappend_clang_resource_dir. Mark the host-only helper with_CCCL_HOST_APIandinline. Update the four call sites in this change.As per coding guidelines, “Use snake_case for all other symbols,” “Functions must be marked with
_CCCL_HOST_API,” and “Non-template, non-constexprfunctions must be declaredinline.”Also applies to: 985-989, 1384-1388, 1537-1541, 2009-2013
Source: Coding guidelines
python/cuda_compute/benchmarks/compute/host/host_benchmark_cases.py (1)
91-107: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winsuggestion:
patch_wrapper_to_skip_native_computesilently no-ops when a wrapper exposes neither_bound_build_resultnorpartitioner. In that case the host-overhead benchmark launches the real kernel against the 1-byte temp storage, so the measurement changes meaning without any signal. The docstring assumes a loud failure, but a silent no-op is also possible if a wrapper type changes its internals. Track whether any patch was applied and raise if none was.-def patch_wrapper_to_skip_native_compute( - wrapper: Any, return_kind: NoopReturnKind -) -> None: +def patch_wrapper_to_skip_native_compute( + wrapper: Any, return_kind: NoopReturnKind +) -> None: @@ + patched = False if (bound := getattr(wrapper, "_bound_build_result", None)) is not None: wrapper._bound_build_result = NoopBuildResult(bound, return_kind) + patched = True if (partitioner := getattr(wrapper, "partitioner", None)) is not None: patch_wrapper_to_skip_native_compute(partitioner, return_kind) + patched = True + + if not patched: + raise RuntimeError( + f"No build result to patch on {type(wrapper).__name__}; " + "host-overhead benchmark would launch the real kernel." + )python/cuda_compute/benchmarks/compute/host/test_host_pytest_benchmark.py (1)
58-95: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winsuggestion: both host-overhead tests leave the patched
NoopBuildResultinside the cached wrapper after the test ends. Any later test or benchmark in the same process that resolves the same cache key then skips the native kernel silently. Clear the caches after the measurement as well, for example with an autouse fixture that callscc.clear_all_caches()on teardown.python/cuda_compute/tests/packaging/test_merge_cuda_wheels.py (1)
50-68: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winsuggestion: the two tests cover only the
changedbranch of_validate_shared_contents. The asserted message pinsmissing=[]andextra=[]as well, so those branches carry the same contract but stay untested. Add one case where a shared file exists in only one extracted wheel, to covermissing/extra.python/cuda_compute/benchmarks/compute/transform/complex_cmp.py (1)
61-74: 🎯 Functional Correctness | 🔵 Trivial | 💤 Low valuesuggestion:
num_elements - 1becomes-1if an axis value of 0 is ever configured, andd_in[:-1]would be empty. The current axis starts at 2^16, so this is not reachable today. Only add a guard if the axis list may change.python/cuda_compute/tests/compute/examples/sort/segmented_sort_object.py (1)
33-63: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winsuggestion:
cp.asarray(start_offsets)andcp.asarray(end_offsets)are re-created six times across construction, sizing, and execution. Each call copies host to device. Hoist the two device arrays once and reuse them; the example then also shows the intended usage.d_output_vals = cp.empty_like(d_input_vals) +d_start_offsets = cp.asarray(start_offsets) +d_end_offsets = cp.asarray(end_offsets)Then pass
start_offsets_in=d_start_offsetsandend_offsets_in=d_end_offsetsin all three call sites.python/cuda_compute/benchmarks/compute/transform/babelstream.py (1)
195-216: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick winsuggestion: Reuse
zip_ininlauncher.ZipIteratorhas no per-invocation mutable state. Constructingzip_in_iteradds host work to each timed launch and makesnstreamtimings inconsistent with the other benchmarks.Source: Path instructions
python/cuda_compute/tests/compute/examples/iterator/reverse_input_iterator.py (1)
27-31: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valuesuggestion: Both reverse-iterator examples carry comments copied from a reduction example, but each calls
cuda.compute.inclusive_scan. The rendered documentation names the wrong operation.
python/cuda_compute/tests/compute/examples/iterator/reverse_input_iterator.py#L27-L31: change "initial value for the reduction" to "initial value for the scan" and "Perform the reduction." to "Perform the scan."python/cuda_compute/tests/compute/examples/iterator/reverse_output_iterator.py#L30-L30: change "Perform the reduction." to "Perform the scan."python/cuda_compute/tests/compute/examples/iterator/shuffle_iterator_basic.py (1)
32-33: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valuesuggestion:
cp.sort(d_output)is compared againstd_inputdirectly. This passes only because the input literal is already sorted ascending. Usecp.sort(d_input)on the right side so the check survives a change to the input values.python/cuda_compute/tests/compute/examples/iterator/zip_iterator_counting.py (1)
35-35: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valuesuggestion:
num_itemsis hardcoded to 8. Uselen(arr)so the example stays correct if the input array changes.python/cuda_compute/tests/compute/test_reduce.py (1)
940-954: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winsuggestion:
test_reduce_with_not_guaranteed_determinismruns the reduction and asserts nothing. Add a loose-tolerance check ond_outputso the test can detect a wrong result, not only an exception.python/cuda_compute/tests/compute/test_scan.py (1)
94-97: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valuesuggestion:
is_short_dtype = dtype.itemsize < 16is true for every dtype exceptcomplex128and wider, so the 31-element truncation applies toint64andfloat64as well. The name and the comment describe small-range types. Either rename the flag to state the real condition, or restrict the truncation to the narrow integer dtypes that can actually overflow.python/cuda_compute/tests/compute/test_permutation_iterator.py (1)
201-220: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winsuggestion:
compile_cpp_op_code.cache_clear()clears process-wide cache state. Other tests then recompile, and thehits >= 2assertion becomes order- and parallelism-sensitive. Prefer a relative measurement instead of clearing the cache.- compile_cpp_op_code.cache_clear() - + baseline = compile_cpp_op_code.cache_info().hits + # Create multiple instances with same structure iterators = [] for i in range(3): @@ - cache_info = compile_cpp_op_code.cache_info() - assert cache_info.hits >= 2, ( - f"Expected cache hits for same structure, got {cache_info.hits} hits, " + cache_info = compile_cpp_op_code.cache_info() + assert cache_info.hits - baseline >= 2, ( + f"Expected cache hits for same structure, got {cache_info.hits - baseline} hits, " f"{cache_info.misses} misses" )python/cuda_compute/tests/compute/test_iterators.py (1)
68-69: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valuesuggestion: Remove the dead assignment.
Line 68 assigns
itand Line 69 overwrites it before any use. Delete Line 68, or compare the kinds of both iterators if that was the intent.- it = CountingIterator(np.int32(0)) it = CountingIterator(np.int32(1))python/cuda_compute/tests/compute/test_binary_search.py (1)
27-37: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winsuggestion: Replace
np.isdtype(dtype, "integral")withnp.issubdtype(dtype, np.integer).cuda-computedeclaresnumpywithout a minimum version, butnp.isdtyperequires NumPy 2.0 or later. NumPy 1.x environments raiseAttributeErrorwhen these tests run.python/cuda_compute/tests/compute/test_bindings.py (1)
120-124: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winsuggestion: Match
TypeInfometadata to the represented types.Valuewraps an 8-byteUINT64; both iterators describeINT32. UseTypeInfo(8, 8, UINT64)andTypeInfo(4, 4, INT32). Iterator state is separate fromvalue_type, but the current metadata still reports incorrect element sizes.python/cuda_compute/tests/compute/test_select.py (1)
35-46: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winsuggestion: the test data is dtype-unaware, so several parameterized select cases assert on empty or full selections instead of partial selections. The shared root cause is that generated values do not respect the predicate ranges for every dtype in
DTYPE_LIST.
python/cuda_compute/tests/compute/test_select.py#L35-L46: scale the floating branch ofrandom_arraybymax_valuewhen it is provided.python/cuda_compute/tests/compute/test_select.py#L301-L301: build signed mixed-sign data fortest_select_reuse_objectinstead of subtracting 50 from unsigned or[0, 1)values.python/cuda_compute/tests/compute/test_serialization_diagnostics.py (1)
48-52: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winsuggestion:
b.find(_C_MAGIC)returns-1when the header layout changes and the magic is absent.struct.pack_intoaccepts a negative offset and then patches bytes near the end of the blob, so the test would fail with a misleading deserialize error instead of pointing at the missing header. Assert the index. The samefindresult is used unguarded at line 77.def _patch_u32(blob, field_off, value): b = bytearray(blob) i = b.find(_C_MAGIC) + assert i >= 0, "C serialization header magic not found in blob" struct.pack_into("<I", b, i + len(_C_MAGIC) + field_off, value) return bytes(b)python/cuda_compute/tests/packaging/test_cuda_compute_metadata.py (1)
159-181: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winsuggestion: these assertions match exact multi-line CMake text, including newlines and two-space indentation. Any reformatting of
c/parallel.v2/CMakeLists.txtor of the computeCMakeLists.txtbreaks the test without a behavior change. Match the intent with whitespace-tolerant regexes instead.+import re ... - assert ( - f'option(\n {runtime_paths}\n "Resolve HostJIT resource paths at runtime ' - 'instead of embedding build-tree defaults."\n OFF\n)' in parallel_cmake - ) - assert ( - f'if (SKBUILD_STATE STREQUAL "wheel")\n set({runtime_paths} ON)\nendif()' - in compute_cmake - ) + assert re.search(rf"option\(\s*{runtime_paths}\s+\"[^\"]+\"\s+OFF\s*\)", parallel_cmake) + assert re.search( + rf"if\s*\(\s*SKBUILD_STATE\s+STREQUAL\s+\"wheel\"\s*\)\s*set\(\s*{runtime_paths}\s+ON\s*\)", + compute_cmake, + )The same concern applies to the exact
set(CYTHON_FLAGS ...)and_cython_package_dirstrings at lines 175-179.python/cuda_compute/tests/test_examples.py (1)
87-93: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winsuggestion:
hasattr(module, "__main__")is never true for an imported module unless the module defines that attribute, so theexecbranch is unreachable. Drop the condition and theexeccall. This also clears the static-analysis code-injection hint on line 93.- # Check if module has a main function - if so, run it - if hasattr(module, "__main__") or hasattr(module, "main"): - # Call main if it exists - if hasattr(module, "main"): - module.main() - else: - # Try to run the module as if it were called directly - exec(f"import {module_name}; {module_name}.__main__()") + # Check if module has a main function - if so, run it + if hasattr(module, "main"): + module.main()Source: Linters/SAST tools
python/cuda_compute/cuda/compute/_bindings_impl.pyx (2)
256-268: 🩺 Stability & Availability | 🔵 Trivial | 💤 Low valuesuggestion: the three
mallocresults are used without NULL checks. On allocation failure the loop writes through a NULL pointer and the process crashes. A partial failure also leaks the earlier two allocations. Check each result and raiseMemoryErrorafter freeing what was already allocated.
193-197: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valuesuggestion: the message states "Alignment must be non-negative" but the check rejects
0, which is non-negative. Use "positive" to match the check and the docstring.Proposed fix
if alignment < 1: raise ValueError( - "Alignment must be non-negative, " + "Alignment must be positive, " f"got {alignment}." )python/cuda_compute/cuda/compute/_caching.py (2)
705-709: 🩺 Stability & Availability | 🔵 Trivial | 💤 Low valuesuggestion:
__eq__accessesother._identityunconditionally. A comparison against a non-CachableFunctionobject raisesAttributeErrorinstead of returningFalse. These objects are used as dict keys, so a hash collision with a foreign key triggers the comparison. ReturnNotImplementedwhen the type does not match.Proposed fix
def __eq__(self, other): + if not isinstance(other, CachableFunction): + return NotImplemented return self._identity == other._identity
490-500: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick winsuggestion: Log
resolve()failures before the fallback rebuildIf
resolve()fails, log the exception at debug level before rebuilding. The defaultbuilder()usescall_build, which returns a loaded result, so the fallback does not need an additionalload()call.python/cuda_compute/cuda/compute/_jit.py (3)
316-365: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winsuggestion:
tuple_to_struct_castregisters ontypes.BaseTuple, which is the base of bothtypes.Tupleandtypes.UniTuple.cast_tuple_to_structthen registers the same conversion for those two subclasses. The two implementations differ in failure behavior: the first raisesValueErroron a size mismatch, the second returnsNone. Keep one implementation so the size-mismatch behavior is deterministic.
938-946: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valuesuggestion: the key name
"strides"holds a single itemsize scalar, not a stride tuple, and"shape"holds a scalar length. The names imply NumPy semantics that do not apply. Rename to"itemsize"/"stride"/"length"or document the 1-D contiguous assumption inline.
82-92: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick winsuggestion: Add an upper bound or explicit compatibility check for
numba-cuda._compile_pyfunc_with_fixupis private, so a future incompatible release can causeImportErrororTypeErrorduring the first compilation.python/cccl_headers/tests/test_cccl_headers.py (1)
121-123: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winsuggestion: this test invokes
cmakewith no availability check. Ifcmakeis absent,subprocess.runraisesFileNotFoundErrorand the test errors instead of skipping. The headers wheel has one runtime dependency and can be tested in a minimal environment. Add a skip guard.def test_cudax_cmake_target_is_consumer_safe(tmp_path): + if shutil.which("cmake") is None: + pytest.skip("cmake is not available") package_root = Path(headers.__file__).parentAdd
import shutilat the top.python/cuda_compute/tests/compute/test_transform.py (1)
86-87: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valuesuggestion:
import numpy as npinsidetest_unary_transform_struct_typeandtest_binary_transform_struct_typeduplicates the module-level import at Line 5. Remove both local imports.Also applies to: 113-114
python/cuda_compute/tests/compute/test_unique_by_key.py (1)
87-110: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valuesuggestion: the loop variable
nextat Line 102 shadows the builtin. Rename it tocurrentornxtto keep the reference host implementation readable.python/cuda_compute/cuda/compute/_utils/__init__.py (1)
8-22: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick winsuggestion: Keep
sanitize_identifieras a character sanitizer._create_void_ptr_wrapperintentionally rejects empty and digit-leading results with.isidentifier(). Update the docstring to state that callers must validate the sanitized result.
There was a problem hiding this comment.
Caution
Inline review comments failed to post. This is likely due to GitHub's internal server error or limits when posting large numbers of comments. If you are seeing this consistently it is likely a permissions issue. Please check "Moderation" -> "Code review limits" under your organization settings.
Actionable comments posted: 1
Note
Due to the large number of review comments, Critical severity comments were prioritized as inline comments.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
docs/python/setup.rst (1)
25-30: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winimportant: Correct the CUDA Toolkit prerequisite.
Lines 25-30 require a preinstalled Toolkit. Lines 45-46 state that
cu12andcu13installcuda-toolkit. State that users need either acu*extra or a compatible system Toolkit. Reserve the system Toolkit prerequisite forsysctk*variants.ci/windows/build_cuda_cccl_python.ps1 (1)
255-272: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick winimportant: forward the coordinated version and
SOURCE_DATE_EPOCHinto the nested CUDA 13 container.Lines 83-114 state the intent that the outer build, the nested CUDA 13 build, and the universal wheel builds share one version. The nested invocation does not pass
SETUPTOOLS_SCM_PRETEND_VERSION_FOR_CUDA_COMPUTEorSOURCE_DATE_EPOCH. The nested script therefore re-derives both from git inside the CUDA 13 image. Any difference in git state, tag visibility, orsafe.directoryhandling in that image produces a different compute wheel version, and the merge step then combines mismatched versions. Forwarding the values also removes the redundant unshallow and version generation in the nested container.Proposed fix
'--env', "CCCL_PYTHON_USE_V2=$($env:CCCL_PYTHON_USE_V2)", + '--env', "SETUPTOOLS_SCM_PRETEND_VERSION_FOR_CUDA_COMPUTE=$($env:SETUPTOOLS_SCM_PRETEND_VERSION_FOR_CUDA_COMPUTE)", + '--env', "SOURCE_DATE_EPOCH=$($env:SOURCE_DATE_EPOCH)", $Cuda13Image,
🟠 Major comments (30)
.github/workflows/release-wheels.yml-67-82 (1)
67-82: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick winMissing Branch Authorization (CWE-862): Missing Authorization
Reachability: Internal · Exploitability: Moderate
important: Restrict accepted runs to approved release branches. The workflow currently accepts successful
workflow_dispatchruns from any branch, so a feature-branch build can reach the PyPI publish step. Add an allowlist for.head_branch, such asmainand approvedbranch/{major}.{minor}.xbranches.ci/test/python_wheels/test_wheel_scripts.py-512-525 (1)
512-525: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick winimportant: the post-uninstall isolation check depends on undefined behavior and can fail for the wrong reason.
importlib.util.find_spec('cuda.compute')imports the parent packagecudafirst. The old metapackage is the only distribution that providescuda/, so after the uninstall two outcomes are possible:
- pip removes the now-empty directories.
import cudathen fails andfind_specraisesModuleNotFoundErrorinstead of returningNone.check=Truereports a test failure.- pip leaves the empty
cuda/compute/directory. That directory resolves as a namespace package, sofind_specreturns a spec and the assertion fails.pip does not guarantee either outcome, so this assertion does not reliably test what it intends. Assert that no importable module with a loader remains, and treat a missing parent as success.
Proposed fix
- ( - "import importlib.util; " - "assert importlib.util.find_spec('cuda.compute') is None; " - "assert importlib.util.find_spec('cuda.cccl') is None" - ), + ( + "import importlib.util\n" + "def gone(name):\n" + " try:\n" + " spec = importlib.util.find_spec(name)\n" + " except ModuleNotFoundError:\n" + " return True\n" + " return spec is None or spec.loader is None\n" + "assert gone('cuda.compute')\n" + "assert gone('cuda.cccl')\n" + ),python/cuda_compute/benchmarks/compute/scan/exclusive/sum.py-82-82 (1)
82-82: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick winimportant: Limit the default element-count axis.
The exponent
32creates2^32elements. Input and output alone require 32 GiB for 32-bit types and 64 GiB for 64-bit types. Temporary storage increases this requirement.Cap the default axis or skip cases that exceed available GPU memory. Otherwise, a normal unfiltered run can terminate before it records the smaller cases.
As per path instructions, benchmark changes must avoid excessive runtime and preserve useful comparison data.
Source: Path instructions
python/cuda_compute/benchmarks/compute/utils.py-53-67 (1)
53-67: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick winimportant: Keep
float64interpolation finite.For
np.float64,max_val - min_valoverflows toinf. Production probabilities then produceinfornan, causing incorrect select thresholds. Use(1.0 - probability) * min_val + probability * max_val.Source: Path instructions
python/cuda_compute/tests/compute/test_no_numba.py-268-270 (1)
268-270: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick winimportant:
cuda-computedeclaresnumpywithout a lower bound, so NumPy 1.x remains supported. Usenp.argsort(h_keys, kind="stable");stable=TrueraisesTypeErroron NumPy 1.x.python/cuda_compute/tests/compute/test_radix_sort.py-322-324 (1)
322-324: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick winimportant: both bit-window tests use
dtype().itemsizeas a bit count.itemsizeis a byte count, so the begin/end bit sets stay within 0..8 and high key bits are never sorted.host_sortrepeats the same bounds, so the assertions pass and the coverage loss is silent.
python/cuda_compute/tests/compute/test_radix_sort.py#L322-L324: changenum_bits = dtype().itemsizetodtype().itemsize * 8intest_radix_sort_pairs_bit_window.python/cuda_compute/tests/compute/test_radix_sort.py#L377-L379: apply the same change intest_radix_sort_pairs_double_buffer_bit_window.python/cuda_compute/tests/test_examples.py-119-127 (1)
119-127: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick winimportant:
subprocess.runhas no timeout. If an example hangs, the whole test session hangs until the CI job is killed. Add a timeout and report it as a failure.result = subprocess.run( [sys.executable, module_file], capture_output=True, text=True, cwd=os.path.dirname(module_file), + timeout=600, )python/cccl_headers/cuda/cccl/headers/include_paths.py-16-23 (1)
16-23: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick winimportant: Make the cached
IncludePathsvalue immutable.
get_include_pathsreturns the same mutable object for each cachedprobe_file. One caller can modify a field and corrupt the include paths returned to later callers.Declare the dataclass with
@dataclass(frozen=True), or cache immutable path values and construct a new result for each call.Also applies to: 50-64
Source: Path instructions
python/cuda_compute/cuda/compute/_bindings_impl.pyx-236-236 (1)
236-236: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick winimportant: Add
except NULLtoget_buffer_pointerWhen
PyObject_GetBufferfails, callers receiveNULLwithout the Python exception being propagated.ctypes_typed_pointer_payload_ptrcan then dereferenceptr_ref[0]and crash.python/cuda_compute/cuda/compute/_jit.py-826-837 (1)
826-837: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick winimportant: the
tryblock catches onlyOSErrorandTypeError. Two other failures are reachable here.ast.parseraisesSyntaxErrorwhen the retrieved source is not a standalone statement, which happens for a lambda:inspect.getsourcereturns the enclosing call expression, andfunc.__name__is<lambda>, so Line 866 would also raiseKeyError.to_jit_op_adapterroutes any callable with captured device arrays through this function, so a lambda that closes over a device array reaches it. Wrap parse and lookup failures in the same actionableValueError, or reject non-defcallables with an explicit message.python/cuda_compute/cuda/compute/_jit.py-668-674 (1)
668-674: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick winimportant: both
OpAdaptersubclasses implement__eq__by dereferencing attributes onotherwith no type check. Python calls__eq__on hash collisions inside dicts and sets, and both classes are registered as cache-key providers at Line 1046 and Line 1048. A collision with any foreign key type raisesAttributeErrorinstead of returningNotImplemented.
python/cuda_compute/cuda/compute/_jit.py#L668-L674: guard_StatelessOp.__eq__withisinstance(other, _StatelessOp)and returnNotImplementedotherwise.python/cuda_compute/cuda/compute/_jit.py#L1027-L1031: guard_StatefulOp.__eq__withisinstance(other, _StatefulOp)and returnNotImplementedotherwise.python/cuda_compute/tests/compute/test_three_way_partition.py-48-50 (1)
48-50: 🚀 Performance & Scalability | 🟠 Major | ⚡ Quick winimportant: this builds 77 parameterizations, and
log_size16 and 20 run 65K and 1M element partitions for all 11 dtypes. Each case compiles two custom operators. The siblingtest_unique_by_key.pymarks sizes at or above2**20withpytest.mark.largethroughget_mark. Apply the same marking here so a default CI run does not pay for 11 million-element partitions.Source: Path instructions
python/cuda_compute/tests/compute/test_unique_by_key.py-143-144 (1)
143-144: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick winimportant: both test files define their own
random_arrayhelper that acceptsmax_valuebut applies it only to integral dtypes. For every floating-point dtype the helper returns values in[0, 1), so the callers' value-based predicates and equality checks degenerate and the float parameterizations lose their coverage. Fix the helper in both places, or move one corrected helper intotests/_utils.
python/cuda_compute/tests/compute/test_unique_by_key.py#L143-L144: scale the float keys into a small integral range so consecutive duplicate keys occur and the de-duplication path runs.python/cuda_compute/tests/compute/test_three_way_partition.py#L87-L93: scale the float inputs across the42threshold so the float cases produce all three partitions.python/cuda_compute/benchmarks/compute/.gitignore-17-19 (1)
17-19: 📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick winimportant: ignoring
pixi.lockleaves the benchmark environment unpinned. A benchmark run on one machine can then resolve different dependency versions than another, which invalidates cross-run performance comparison. The benchmarking conventions require reproducible runs and comparable results. Commitpixi.lockand ignore only.pixi/.# Pixi .pixi/ -pixi.lockAs per path instructions: "Check that benchmark changes measure meaningful workloads, keep axes comparable, avoid excessive runtime, and preserve useful comparison data."
Source: Path instructions
python/cccl_headers/tests/test_cccl_headers.py-249-252 (1)
249-252: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick winimportant: Line 250, Line 251, Line 293, Line 294, and Line 295 pass CMake cache values through
.as_posix(), but Line 249 and Line 252 interpolate the rawPath. On Windowsstr(Path)produces backslashes, and CMake treats a backslash in a cache value as an escape. The result is a brokencudax_DIRandCMAKE_INSTALL_PREFIX. Use.as_posix()for both.- f"-Dcudax_DIR={cudax_cmake_dir}", + f"-Dcudax_DIR={cudax_cmake_dir.as_posix()}", f"-DCCCL_DIR={(package_root / 'lib' / 'cmake' / 'cccl').as_posix()}", f"-DEXPECTED_CUDAX_INCLUDE={(package_root / 'include').as_posix()}", - f"-DCMAKE_INSTALL_PREFIX={build_dir / 'install'}", + f"-DCMAKE_INSTALL_PREFIX={(build_dir / 'install').as_posix()}",python/cuda_compute/cuda/compute/struct.py-97-107 (1)
97-107: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick winimportant: Do not discard constructor arguments. Positional arguments cause all keyword arguments to be ignored. A dictionary first argument also causes trailing positional arguments to be ignored. Reject unsupported mixes, or merge them with duplicate and missing-field validation.
python/cuda_compute/cuda/compute/struct.py-146-163 (1)
146-163: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick winimportant: Add coercion for declared
TypeDescriptorfields._field_spec_to_type_descriptorsacceptstypes.TypeDescriptor, but_coerce_valuehas no matching branch.gpu_struct({"x": types.int32})(1)reaches the fallback and raisesAttributeError. Validate nested struct classes before accepting them, and coerce descriptors throughfield_type.dtype.type(...).python/cuda_compute/cuda/compute/types.py-74-80 (1)
74-80: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick winimportant: Freeze the field mapping at construction.
StructTypeDescriptorstores and exposes the caller’s mutable dictionary, while equality, hashing, andlayout_key()read it. A later mutation changes cache identity while_dtyperemains unchanged. Copy the mapping and expose an immutable view.Also applies to: 90-108
python/cuda_compute/cuda/compute/types.py-260-265 (1)
260-265: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick winimportant: Collect every annotated positional parameter. The
breakleavesinput_tdswith only the first annotated parameter. A multi-argument operator then gets a truncated signature. Remove thebreakand define explicit handling for missing annotations.python/cuda_compute/cuda/compute/types.py-171-179 (1)
171-179: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick winimportant: Preserve structured-dtype layouts
from_numpy_dtype()andgpu_struct()rebuild structured dtypes from field types withalign=True. This discards source offsets anditemsize, so packed or explicitly padded dtypes can receive a different record ABI. Preserve the source layout, or reject unsupported layouts, in both paths.gpu_struct()must also handle title aliases without treating them as additional fields.ci/windows/test_cuda_compute_python.ps1-29-38 (1)
29-38: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick winimportant: Install only
"$wheelPath[test-$ctkFlavor$cudaMajor]"and let pip resolvecccl-headersfrom$wheelhouse. The current command installs$headersWheelPathseparately, so the import assertion cannot detect a missing dependency incuda-compute. Run this check in a fresh environment and assert the installedcccl-headersdistribution.python/cuda_compute/cuda/compute/iterators/_discard.py-70-84 (1)
70-84: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick winimportant: Do not expose the discard iterator as input-capable. This no-op operation does not write
result, butIteratorBasetreats its non-Nonereturn as a valid input dereference. An algorithm that receivesDiscardIteratorasd_inreads uninitialized device data. ReturnNonehere so input conversion fails.python/cuda_compute/cuda/compute/iterators/_reverse.py-37-41 (1)
37-41: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick winimportant: Define empty-array behavior before calculating
size - 1. For an empty device array, this creates aPointerIteratorat-1elements and serializes a pointer before the allocation. Reject the input or use a valid empty-range representation.python/cuda_compute/cuda/compute/iterators/_reverse.py-139-140 (1)
139-140: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick winimportant: Negate the host-side offset. The class contract and
_make_advance_op()move positive offsets backward. This method moves the wrapped iterator forward instead. For an array-backed iterator,ReverseIterator(array) + 1points one element past the end instead of at the preceding element.python/cuda_compute/cuda/compute/iterators/_base.py-76-81 (1)
76-81: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick winimportant: Resolve the implicit iterator cache key to the current compute capability. When
USING_V2is false,get_target_cc()returnsNonefor default builds, and all three accessors useNoneas the cache key. Switching devices can reuse the first device's incompatible LTO-IR.python/cuda_compute/cuda/compute/iterators/_cache_modified.py-69-78 (1)
69-78: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick winimportant: Reject descriptors without a generated C++ type. A 4-byte structured dtype passes the size check, but
cpp_type_from_descriptor()returnsNone, producingstatic_cast<None**>and a JIT compilation failure.python/cuda_compute/cuda/compute/iterators/_cache_modified.py-96-100 (1)
96-100: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick winimportant: Use
int64_tfor the advance distance.ReverseIteratorpasses a negativeint64_tto the wrapped iterator. Reading it asuint64_tconverts-nto2^64 - n, causing an invalid positive pointer adjustment.python/cuda_compute/cuda/compute/_utils/temp_storage_buffer.py-20-40 (1)
20-40: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick winimportant: Bind memory-pool configuration and default-stream cleanup to the allocation device. When
streamtargets another device,_set_default_mempool_threshold(dev.device_id)configures the wrong pool. WhenstreamisNone, finalization can enqueuecudaFreeAsyncon another thread's default device and leave the allocation reserved after the warning. Store the allocation device and preserve the allocation stream or device for cleanup. Add multi-GPU tests for cross-device streams and cross-thread finalization.ci/windows/test_cuda_compute_minimal_python.ps1-29-37 (1)
29-37: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick winimportant: Do not pass
$headersWheelPathas a direct install target. The command installscccl-headerseven if thecuda-computewheel omits its required dependency. Becausepip checkvalidates declared installed dependencies, it cannot detect that omission. Install only"$wheelPath[minimal-$ctkFlavor$cudaMajor]"with--find-links $wheelhouse, then assert that the resolver installed the matchingcccl-headersdistribution. (pip.pypa.io)Based on PR objectives, the split requires dependency validation across the wheel set.
python/cuda_compute/README.md-17-19 (1)
17-19: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick winimportant: Add the required cleanup and reinstall procedure to the installation section. The README directs existing aggregate users to continue installing
cuda-ccclbut does not provide the required migration from the monolithic wheel. Users can otherwise retain files from the old wheel-ownership layout. As per PR objectives, the split requires an uninstall/reinstall procedure when users upgrade from the monolithic wheel.
🟡 Minor comments (21)
docs/python/compute/index.rst-311-311 (1)
311-311: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winimportant: Quote the package extras in these installation commands.
Unquoted square brackets can be interpreted as pathname patterns by the shell. Use quoted requirements such as
pip install 'cuda-compute[minimal-cu13]'andpip install 'cuda-compute[minimal-sysctk13]'.Also applies to: 541-542
Source: Path instructions
ci/util/python/validate_cccl_wheel_set.py-71-75 (1)
71-75: 🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick winimportant: Parse extra markers instead of matching one rendered form. A valid marker such as
(python_version < "3.11") and extra == "test"or a changed version bound is classified as unconditional, which can block wheel validation. Use a complete marker parser and add regression tests for these forms.python/cuda_compute/benchmarks/compute/partition/three_way.py-67-68 (1)
67-68: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick winimportant: Report both counter writes.
d_num_selected_outstores two counters, but the accounting reports one. Use2 * d_num_selected_out.dtype.itemsizeto match C++add_global_memory_writes<offset_t>(2).np.int32is a supported output dtype and does not cause an out-of-bounds write.python/cuda_compute/tests/compute/examples/sort/radix_sort_basic.py-45-47 (1)
45-47: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick winimportant: Replace
stable=Truewithkind="stable"in both sort examples.python/cuda_compute/pyproject.tomldeclaresnumpywithout a minimum version, and NumPy 1.x raisesTypeErrorforstable=True.python/cuda_compute/benchmarks/compute/segmented_reduce/variable_sum.py-118-118 (1)
118-118: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winimportant: Report both offset arrays in segmented-operation metrics.
Both benchmarks pass separate start and end offset arrays but report traffic for one contiguous
num_segments + 1array.
python/cuda_compute/benchmarks/compute/segmented_reduce/variable_sum.py#L118-L118: report2 * num_segments * start_offsets.dtype.itemsize.python/cuda_compute/benchmarks/compute/segmented_sort/keys.py#L126-L126: report2 * num_segments * start_offsets.dtype.itemsize.As per path instructions, benchmark results must preserve useful comparison data.
Source: Path instructions
python/cuda_compute/tests/compute/examples/select/select_object.py-57-78 (1)
57-78: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winimportant: Validate every demonstrated selection result.
Both examples execute and retrieve an output that the assertions do not validate.
python/cuda_compute/tests/compute/examples/select/select_object.py#L57-L78: assert the second selected count and values.python/cuda_compute/tests/compute/examples/select/select_with_side_effect.py#L39-L54: assert the selected even values, not only the counts.python/cuda_compute/tests/compute/examples/sort/radix_sort_buffer.py-21-23 (1)
21-23: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick winsuggestion:
8.3 - 1in the literal list looks like a typo for8.3, -1. The list still has 10 entries, so the example passes, but a reader of a public example will stop here. Replace it with the intended literal.h_in_values = np.array( - [-3.2, 2.2, 1.9, 4.0, -3.9, 2.7, 0, 8.3 - 1, 2.9, 5.4], dtype="float32" + [-3.2, 2.2, 1.9, 4.0, -3.9, 2.7, 0, 7.3, 2.9, 5.4], dtype="float32" )python/cuda_compute/benchmarks/compute/transform/complex_cmp.py-84-86 (1)
84-86: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winimportant: the write accounting is off by one element. The transform writes
num_items = num_elements - 1booleans, but line 86 chargesnum_elements. Usenum_itemsso the reported write bandwidth matches the C++ benchmark.state.add_element_count(num_elements) state.add_global_memory_reads(num_elements * d_in.dtype.itemsize) - state.add_global_memory_writes(num_elements * d_out.dtype.itemsize) + state.add_global_memory_writes(num_items * d_out.dtype.itemsize)Note
num_itemsis currently defined at line 79, after this block would need it; move the assignment abovestate.add_element_count.Source: Path instructions
python/cuda_compute/tests/compute/examples/sort/radix_sort_buffer.py-49-49 (1)
49-49: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick winimportant: Use
kind="stable"or require NumPy 2.0+.cuda-computedeclares an unconstrainednumpydependency, so NumPy 1.x remains possible;stable=TrueraisesTypeErrorthere.python/cuda_compute/tests/compute/test_reduce.py-473-487 (1)
473-487: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winimportant: This block builds
reducer_1andreducer_2for two identical-body ops with different names, then never asserts anything. The case is dead code and covers nothing.Add the expected assertion. Based on the neighbouring cases and the comment, ops with the same body but different names should map to the same cached reducer:
reducer_2 = cuda.compute.make_reduce_into( d_in=TransformIterator(CountingIterator(np.int32(0)), op3), d_out=DeviceArray.empty(1, dtype="int64"), op=sum_op, h_init=np.zeros(1, dtype="int64"), ) + assert reducer_1 is reducer_2If the intended behaviour is the opposite, use
is notinstead. Confirm the caching contract before choosing.python/cuda_compute/tests/compute/examples/raw_op/llvm_stateless.py-13-22 (1)
13-22: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick winimportant: Exclude
llvmlite 0.43.xfrom the example test dependencies or rewrite the IR with typed pointers.llvmlite 0.43.xdoes not support the opaqueptrsyntax used here. The example runner already handlessys.exit(0), andllvm.initialize()is not required by currentllvmlitereleases.python/cuda_compute/tests/compute/examples/scan/running_average.py-1-16 (1)
1-16: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick winimportant: Add the license header and move
# example-beginabove the imports.This file has no NVIDIA copyright/SPDX header. Every sibling example in
python/cuda_compute/tests/compute/examples/scan/carries one, and the repository guidelines require the license text in all source files.The
# example-beginmarker at Line 12 also sits after the imports. Documentation extraction betweenexample-beginandexample-endtherefore produces a snippet withoutimport cupy,import numpy, or thecuda.computeimports. The docstring at Lines 13-15 is likewise not a module docstring because statements precede it.As per coding guidelines: "All headers, and also source files, must have the correct license text."
+# Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +# example-begin +""" +Inclusive scan using zip iterator and output transform iterator to compute running average. +""" + import cupy as cp import numpy as np import cuda.compute from cuda.compute import ( ConstantIterator, TransformOutputIterator, ZipIterator, gpu_struct, ) - -# example-begin -""" -Inclusive scan using zip iterator and output transform iterator to compute running average. -"""Source: Coding guidelines
python/cuda_compute/tests/compute/test_iterators.py-107-118 (1)
107-118: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick winimportant:
test_reverse_output_iterator_equalityduplicates the input test.ReverseIteratorsupports both input and output dereference, but this test only compareskindand does not exercise output dereference. Delete the duplicate or replace it with an output-operation test.python/cuda_compute/cuda/compute/_jit.py-818-820 (1)
818-820: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick winsuggestion: the docstring says the state arrays appear "after the regular parameters".
_AddStateParametersprepends them, and the example at Line 812 showsdef func(state, x). Correct the docstring to say the state parameters come first.python/cuda_compute/.gitignore-30-31 (1)
30-31: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick winsuggestion: Replace
*.cmakewith explicit generated-file patterns such ascmake_install.cmakeandCTestTestfile.cmake. Remove the no-op!CMakeLists.txtrule.python/cuda_compute/cuda/compute/_utils/protocols.py-73-85 (1)
73-85: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick winimportant: Catch
TypeErroras well asAttributeError. PyTorch exposesTensor.sizeas a method, soint(arr.size)raisesTypeErrorbefore the shape fallback.python/cuda_compute/cuda/compute/struct.py-58-63 (1)
58-63: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winimportant: Reject Python keywords and names defined by or required by
_Struct._jit.pygenerates invalid attribute expressions for keyword fields, and_fieldsoverwrites the instance field map before record conversion.python/cuda_compute/cuda/compute/typing.py-64-64 (1)
64-64: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winimportant: Add
StreamLiketo__all__. The protocol is public and documented, but wildcard imports and API tools that honor__all__omit it.python/cuda_compute/cuda/compute/typing.py-61-61 (1)
61-61: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick winimportant: Close the
RawOpcross-reference target. The missing>prevents Sphinx from resolvingcuda.compute.op.RawOp.python/cuda_cccl/README.md-11-14 (1)
11-14: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winimportant: Quote both extras specifiers. In zsh, unquoted square brackets can cause
no matches foundbeforepipruns. Usepip install "cuda-cccl[cu13]"andpip install "cuda-cccl[cu12]".python/cuda_compute/README.md-22-23 (1)
22-23: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winimportant: Quote every requirement that contains an extra. Unquoted
[...]is shell glob syntax. A shell can alter or reject the requirement beforepipparses it. Use"cuda-compute[cu13]"and the same quoting for all variants, including the aggregate example. (pip.pypa.io)Also applies to: 30-31, 37-38, 42-42
🧹 Nitpick comments (31)
c/parallel.v2/src/hostjit/libnvcc/compiler.cpp (1)
135-141: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winimportant: Apply the required CCCL C++ naming and function declarations.
Rename
appendClangResourceDirtoappend_clang_resource_dir. Mark the host-only helper with_CCCL_HOST_APIandinline. Update the four call sites in this change.As per coding guidelines, “Use snake_case for all other symbols,” “Functions must be marked with
_CCCL_HOST_API,” and “Non-template, non-constexprfunctions must be declaredinline.”Also applies to: 985-989, 1384-1388, 1537-1541, 2009-2013
Source: Coding guidelines
python/cuda_compute/benchmarks/compute/host/host_benchmark_cases.py (1)
91-107: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winsuggestion:
patch_wrapper_to_skip_native_computesilently no-ops when a wrapper exposes neither_bound_build_resultnorpartitioner. In that case the host-overhead benchmark launches the real kernel against the 1-byte temp storage, so the measurement changes meaning without any signal. The docstring assumes a loud failure, but a silent no-op is also possible if a wrapper type changes its internals. Track whether any patch was applied and raise if none was.-def patch_wrapper_to_skip_native_compute( - wrapper: Any, return_kind: NoopReturnKind -) -> None: +def patch_wrapper_to_skip_native_compute( + wrapper: Any, return_kind: NoopReturnKind +) -> None: @@ + patched = False if (bound := getattr(wrapper, "_bound_build_result", None)) is not None: wrapper._bound_build_result = NoopBuildResult(bound, return_kind) + patched = True if (partitioner := getattr(wrapper, "partitioner", None)) is not None: patch_wrapper_to_skip_native_compute(partitioner, return_kind) + patched = True + + if not patched: + raise RuntimeError( + f"No build result to patch on {type(wrapper).__name__}; " + "host-overhead benchmark would launch the real kernel." + )python/cuda_compute/benchmarks/compute/host/test_host_pytest_benchmark.py (1)
58-95: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winsuggestion: both host-overhead tests leave the patched
NoopBuildResultinside the cached wrapper after the test ends. Any later test or benchmark in the same process that resolves the same cache key then skips the native kernel silently. Clear the caches after the measurement as well, for example with an autouse fixture that callscc.clear_all_caches()on teardown.python/cuda_compute/tests/packaging/test_merge_cuda_wheels.py (1)
50-68: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winsuggestion: the two tests cover only the
changedbranch of_validate_shared_contents. The asserted message pinsmissing=[]andextra=[]as well, so those branches carry the same contract but stay untested. Add one case where a shared file exists in only one extracted wheel, to covermissing/extra.python/cuda_compute/benchmarks/compute/transform/complex_cmp.py (1)
61-74: 🎯 Functional Correctness | 🔵 Trivial | 💤 Low valuesuggestion:
num_elements - 1becomes-1if an axis value of 0 is ever configured, andd_in[:-1]would be empty. The current axis starts at 2^16, so this is not reachable today. Only add a guard if the axis list may change.python/cuda_compute/tests/compute/examples/sort/segmented_sort_object.py (1)
33-63: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winsuggestion:
cp.asarray(start_offsets)andcp.asarray(end_offsets)are re-created six times across construction, sizing, and execution. Each call copies host to device. Hoist the two device arrays once and reuse them; the example then also shows the intended usage.d_output_vals = cp.empty_like(d_input_vals) +d_start_offsets = cp.asarray(start_offsets) +d_end_offsets = cp.asarray(end_offsets)Then pass
start_offsets_in=d_start_offsetsandend_offsets_in=d_end_offsetsin all three call sites.python/cuda_compute/benchmarks/compute/transform/babelstream.py (1)
195-216: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick winsuggestion: Reuse
zip_ininlauncher.ZipIteratorhas no per-invocation mutable state. Constructingzip_in_iteradds host work to each timed launch and makesnstreamtimings inconsistent with the other benchmarks.Source: Path instructions
python/cuda_compute/tests/compute/examples/iterator/reverse_input_iterator.py (1)
27-31: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valuesuggestion: Both reverse-iterator examples carry comments copied from a reduction example, but each calls
cuda.compute.inclusive_scan. The rendered documentation names the wrong operation.
python/cuda_compute/tests/compute/examples/iterator/reverse_input_iterator.py#L27-L31: change "initial value for the reduction" to "initial value for the scan" and "Perform the reduction." to "Perform the scan."python/cuda_compute/tests/compute/examples/iterator/reverse_output_iterator.py#L30-L30: change "Perform the reduction." to "Perform the scan."python/cuda_compute/tests/compute/examples/iterator/shuffle_iterator_basic.py (1)
32-33: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valuesuggestion:
cp.sort(d_output)is compared againstd_inputdirectly. This passes only because the input literal is already sorted ascending. Usecp.sort(d_input)on the right side so the check survives a change to the input values.python/cuda_compute/tests/compute/examples/iterator/zip_iterator_counting.py (1)
35-35: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valuesuggestion:
num_itemsis hardcoded to 8. Uselen(arr)so the example stays correct if the input array changes.python/cuda_compute/tests/compute/test_reduce.py (1)
940-954: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winsuggestion:
test_reduce_with_not_guaranteed_determinismruns the reduction and asserts nothing. Add a loose-tolerance check ond_outputso the test can detect a wrong result, not only an exception.python/cuda_compute/tests/compute/test_scan.py (1)
94-97: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valuesuggestion:
is_short_dtype = dtype.itemsize < 16is true for every dtype exceptcomplex128and wider, so the 31-element truncation applies toint64andfloat64as well. The name and the comment describe small-range types. Either rename the flag to state the real condition, or restrict the truncation to the narrow integer dtypes that can actually overflow.python/cuda_compute/tests/compute/test_permutation_iterator.py (1)
201-220: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winsuggestion:
compile_cpp_op_code.cache_clear()clears process-wide cache state. Other tests then recompile, and thehits >= 2assertion becomes order- and parallelism-sensitive. Prefer a relative measurement instead of clearing the cache.- compile_cpp_op_code.cache_clear() - + baseline = compile_cpp_op_code.cache_info().hits + # Create multiple instances with same structure iterators = [] for i in range(3): @@ - cache_info = compile_cpp_op_code.cache_info() - assert cache_info.hits >= 2, ( - f"Expected cache hits for same structure, got {cache_info.hits} hits, " + cache_info = compile_cpp_op_code.cache_info() + assert cache_info.hits - baseline >= 2, ( + f"Expected cache hits for same structure, got {cache_info.hits - baseline} hits, " f"{cache_info.misses} misses" )python/cuda_compute/tests/compute/test_iterators.py (1)
68-69: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valuesuggestion: Remove the dead assignment.
Line 68 assigns
itand Line 69 overwrites it before any use. Delete Line 68, or compare the kinds of both iterators if that was the intent.- it = CountingIterator(np.int32(0)) it = CountingIterator(np.int32(1))python/cuda_compute/tests/compute/test_binary_search.py (1)
27-37: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winsuggestion: Replace
np.isdtype(dtype, "integral")withnp.issubdtype(dtype, np.integer).cuda-computedeclaresnumpywithout a minimum version, butnp.isdtyperequires NumPy 2.0 or later. NumPy 1.x environments raiseAttributeErrorwhen these tests run.python/cuda_compute/tests/compute/test_bindings.py (1)
120-124: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winsuggestion: Match
TypeInfometadata to the represented types.Valuewraps an 8-byteUINT64; both iterators describeINT32. UseTypeInfo(8, 8, UINT64)andTypeInfo(4, 4, INT32). Iterator state is separate fromvalue_type, but the current metadata still reports incorrect element sizes.python/cuda_compute/tests/compute/test_select.py (1)
35-46: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winsuggestion: the test data is dtype-unaware, so several parameterized select cases assert on empty or full selections instead of partial selections. The shared root cause is that generated values do not respect the predicate ranges for every dtype in
DTYPE_LIST.
python/cuda_compute/tests/compute/test_select.py#L35-L46: scale the floating branch ofrandom_arraybymax_valuewhen it is provided.python/cuda_compute/tests/compute/test_select.py#L301-L301: build signed mixed-sign data fortest_select_reuse_objectinstead of subtracting 50 from unsigned or[0, 1)values.python/cuda_compute/tests/compute/test_serialization_diagnostics.py (1)
48-52: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winsuggestion:
b.find(_C_MAGIC)returns-1when the header layout changes and the magic is absent.struct.pack_intoaccepts a negative offset and then patches bytes near the end of the blob, so the test would fail with a misleading deserialize error instead of pointing at the missing header. Assert the index. The samefindresult is used unguarded at line 77.def _patch_u32(blob, field_off, value): b = bytearray(blob) i = b.find(_C_MAGIC) + assert i >= 0, "C serialization header magic not found in blob" struct.pack_into("<I", b, i + len(_C_MAGIC) + field_off, value) return bytes(b)python/cuda_compute/tests/packaging/test_cuda_compute_metadata.py (1)
159-181: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winsuggestion: these assertions match exact multi-line CMake text, including newlines and two-space indentation. Any reformatting of
c/parallel.v2/CMakeLists.txtor of the computeCMakeLists.txtbreaks the test without a behavior change. Match the intent with whitespace-tolerant regexes instead.+import re ... - assert ( - f'option(\n {runtime_paths}\n "Resolve HostJIT resource paths at runtime ' - 'instead of embedding build-tree defaults."\n OFF\n)' in parallel_cmake - ) - assert ( - f'if (SKBUILD_STATE STREQUAL "wheel")\n set({runtime_paths} ON)\nendif()' - in compute_cmake - ) + assert re.search(rf"option\(\s*{runtime_paths}\s+\"[^\"]+\"\s+OFF\s*\)", parallel_cmake) + assert re.search( + rf"if\s*\(\s*SKBUILD_STATE\s+STREQUAL\s+\"wheel\"\s*\)\s*set\(\s*{runtime_paths}\s+ON\s*\)", + compute_cmake, + )The same concern applies to the exact
set(CYTHON_FLAGS ...)and_cython_package_dirstrings at lines 175-179.python/cuda_compute/tests/test_examples.py (1)
87-93: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winsuggestion:
hasattr(module, "__main__")is never true for an imported module unless the module defines that attribute, so theexecbranch is unreachable. Drop the condition and theexeccall. This also clears the static-analysis code-injection hint on line 93.- # Check if module has a main function - if so, run it - if hasattr(module, "__main__") or hasattr(module, "main"): - # Call main if it exists - if hasattr(module, "main"): - module.main() - else: - # Try to run the module as if it were called directly - exec(f"import {module_name}; {module_name}.__main__()") + # Check if module has a main function - if so, run it + if hasattr(module, "main"): + module.main()Source: Linters/SAST tools
python/cuda_compute/cuda/compute/_bindings_impl.pyx (2)
256-268: 🩺 Stability & Availability | 🔵 Trivial | 💤 Low valuesuggestion: the three
mallocresults are used without NULL checks. On allocation failure the loop writes through a NULL pointer and the process crashes. A partial failure also leaks the earlier two allocations. Check each result and raiseMemoryErrorafter freeing what was already allocated.
193-197: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valuesuggestion: the message states "Alignment must be non-negative" but the check rejects
0, which is non-negative. Use "positive" to match the check and the docstring.Proposed fix
if alignment < 1: raise ValueError( - "Alignment must be non-negative, " + "Alignment must be positive, " f"got {alignment}." )python/cuda_compute/cuda/compute/_caching.py (2)
705-709: 🩺 Stability & Availability | 🔵 Trivial | 💤 Low valuesuggestion:
__eq__accessesother._identityunconditionally. A comparison against a non-CachableFunctionobject raisesAttributeErrorinstead of returningFalse. These objects are used as dict keys, so a hash collision with a foreign key triggers the comparison. ReturnNotImplementedwhen the type does not match.Proposed fix
def __eq__(self, other): + if not isinstance(other, CachableFunction): + return NotImplemented return self._identity == other._identity
490-500: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick winsuggestion: Log
resolve()failures before the fallback rebuildIf
resolve()fails, log the exception at debug level before rebuilding. The defaultbuilder()usescall_build, which returns a loaded result, so the fallback does not need an additionalload()call.python/cuda_compute/cuda/compute/_jit.py (3)
316-365: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winsuggestion:
tuple_to_struct_castregisters ontypes.BaseTuple, which is the base of bothtypes.Tupleandtypes.UniTuple.cast_tuple_to_structthen registers the same conversion for those two subclasses. The two implementations differ in failure behavior: the first raisesValueErroron a size mismatch, the second returnsNone. Keep one implementation so the size-mismatch behavior is deterministic.
938-946: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valuesuggestion: the key name
"strides"holds a single itemsize scalar, not a stride tuple, and"shape"holds a scalar length. The names imply NumPy semantics that do not apply. Rename to"itemsize"/"stride"/"length"or document the 1-D contiguous assumption inline.
82-92: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick winsuggestion: Add an upper bound or explicit compatibility check for
numba-cuda._compile_pyfunc_with_fixupis private, so a future incompatible release can causeImportErrororTypeErrorduring the first compilation.python/cccl_headers/tests/test_cccl_headers.py (1)
121-123: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winsuggestion: this test invokes
cmakewith no availability check. Ifcmakeis absent,subprocess.runraisesFileNotFoundErrorand the test errors instead of skipping. The headers wheel has one runtime dependency and can be tested in a minimal environment. Add a skip guard.def test_cudax_cmake_target_is_consumer_safe(tmp_path): + if shutil.which("cmake") is None: + pytest.skip("cmake is not available") package_root = Path(headers.__file__).parentAdd
import shutilat the top.python/cuda_compute/tests/compute/test_transform.py (1)
86-87: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valuesuggestion:
import numpy as npinsidetest_unary_transform_struct_typeandtest_binary_transform_struct_typeduplicates the module-level import at Line 5. Remove both local imports.Also applies to: 113-114
python/cuda_compute/tests/compute/test_unique_by_key.py (1)
87-110: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valuesuggestion: the loop variable
nextat Line 102 shadows the builtin. Rename it tocurrentornxtto keep the reference host implementation readable.python/cuda_compute/cuda/compute/_utils/__init__.py (1)
8-22: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick winsuggestion: Keep
sanitize_identifieras a character sanitizer._create_void_ptr_wrapperintentionally rejects empty and digit-leading results with.isidentifier(). Update the docstring to state that callers must validate the sanitized result.
🛑 Comments failed to post (1)
python/cuda_compute/tests/compute/test_transform.py (1)
639-663: 🗄️ Data Integrity & Integration | 🔴 Critical | ⚡ Quick win
critical: this test documents a gap in the stateful cache key.
_JitOpState.get_cache_key()in_jit.pyreturns only the state names and dtypes. It omits the array length._compile_stateful_opbakeslen(state_array)intostate_info["shape"], solen(arr)is a compile-time constant in the generated code.op1andop2here share the same namearr, the samefloat64dtype, and the same bytecode, so their cache keys are equal while their compiled code must differ.The test passes today only because
_StatefulOp.__eq__compares_stateby identity and therefore never hits the cache. Add the array length to_JitOpState.get_cache_key()before changing that equality.
ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Enterprise Run ID: 📒 Files selected for processing (295)
💤 Files with no reviewable changes (9)
📝 WalkthroughSummary by CodeRabbit
WalkthroughChangesPython package split and implementation
Possibly related PRs
Suggested reviewers: Comment |
⏱️ CCCL compile-time benchmark comparison: Public headers compile-time benchResult: 0 regression row(s), 10 improvement row(s) above threshold.
Artifacts: reports and traces TU total compilation
🟢 TU total compilation — Improvements
Direct file processing
🟢 Direct file processing — Improvements
|
😬 CI Workflow Results🟥 Finished in 3h 41m: Pass: 78%/534 | Total: 6d 19h | Max: 1h 29m | Hits: 94%/1272772See results here. AI failure analysis1. Python 3.8 cannot parse parenthesized context managers · 4 jobsExplanation: Four packaging jobs fail before running the new wheel validation tests because the test file uses syntax introduced after Python 3.8. The packaging environment explicitly selects Python 3.8.10. Evidence: CCCL Packaging nvcc Clang / [CTK12.0 Clang14] Test(amd64, RTX2080): -min-cmake, step 4 Root cause: The PR added two parenthesized multi-context-manager statements, which Python 3.8 cannot parse. These tests are executed by packaging lanes whose discovered interpreter is Python 3.8.10. Sources: ci/test/python_wheels/test_wheel_scripts.py:311, ci/test/python_wheels/test_wheel_scripts.py:436. Suggested next steps: Rewrite both statements using Python 3.8-compatible nested or unparenthesized `with` statements, then run `python3.8 ci/test/python_wheels/test_wheel_scripts.py`. Copy this prompt into a coding agentJobs: 2. Unused start_block variable is promoted to a CUDA error · 37 jobsExplanation: All cudax NVCC build matrices stop because `start_block` is assigned but never meaningfully used, and warning 550-D is promoted to an error. This blocks Clang, GCC, amd64, arm64, C++17, and C++20 configurations alike. Evidence: cudax nvcc Clang / [CTK12.9 Clang14 C++20] Build(amd64), step 4 cudax nvcc GCC / [CTK12.9 GCC14 C++17] Build(amd64), step 4 Root cause: `localized_array.cuh` computes `start_block` but does not include it in the allocation-map output or otherwise consume its value. NVCC still diagnoses it as set-but-unused despite the void cast, and the build promotes that warning to an error. Sources: cudax/include/cuda/experimental/__places/localized_array.cuh:471, cudax/include/cuda/experimental/__places/localized_array.cuh:472. Suggested next steps: Remove `start_block` and its void cast unless the allocation map is intended to display it. Rebuild a single failing target such as `cudax.test.places.stream_pool` with warnings promoted before rerunning the matrix. Copy this prompt into a coding agentJobs:
3. Wheel build script tries to delete a mounted directory · 6 jobsExplanation: Six Python wheel jobs exit immediately when startup cleanup attempts to remove the `wheelhouse` directory itself. CI mounts that path as a Docker volume, so the mount point cannot be unlinked. Evidence: Python nvcc GCC / Yk / [CTK12.9 GCC13 py3.10] Build CCCL Python wheels(amd64), step 4 Root cause: The PR changed startup cleanup to `rm -rf wheelhouse wheelhouse_merged wheelhouse_final`. The CI container mounts `wheelhouse` as a named volume, making removal of the directory itself fail under `set -e`. Sources: ci/build_cuda_cccl_python.sh:61. Suggested next steps: Preserve the mounted `wheelhouse` directory and delete only its contents, while continuing to remove the unmounted intermediate directories. Validate with `./ci/build_cuda_cccl_python.sh -py-version 3.14` inside the CI devcontainer. Copy this prompt into a coding agentJobs:
4. CMake scratch paths exceed Windows MAX_PATH · 12 jobsExplanation: Twelve Windows header-package jobs fail while configuring the new cudax consumer-safety test. The pytest temporary directory, descriptive test name, build suffix, and CMake TryCompile tree combine into an MSBuild path longer than 260 characters. Evidence: Python nvcc MSVC / cc / [CTK12.0 MSVC14.44 py3.14] Test cuda.cccl.headers(amd64, L4), step 4 Python nvcc MSVC / b8 / [CTK12.9 MSVC14.44 py3.10] Test cuda.cccl.headers(amd64, L4), step 4 Root cause: The new test creates `build-False` and `build-True` beneath pytest's already long per-test temporary path. Visual Studio's generated CMake compiler-check files then exceed the runner's effective 260-character path limit. Sources: python/cccl_headers/tests/test_cccl_headers.py:240, python/cccl_headers/tests/test_cccl_headers.py:247. Suggested next steps: Use a substantially shorter build root and directory names on Windows, such as a short temporary root plus `b0` and `b1`, with reliable cleanup. Run `pytest -q python/cccl_headers/tests/test_cccl_headers.py::test_cudax_cmake_target_is_consumer_safe` on Windows. Copy this prompt into a coding agentJobs:
5. Select example subprocess exits without diagnostics on Windows · 1 jobExplanation: The Windows cuda.compute example suite passes 69 tests but the select-basic module fails when launched as a separate script. The harness reports an empty stderr and omits the subprocess return code, so the log cannot distinguish a Python exception from a native process crash. Evidence: Root cause: The saved log proves the module succeeds during in-process discovery but its script subprocess returns nonzero with no stderr. The actual subprocess exit code and stdout are not logged, so evidence is insufficient to identify whether the cause is a Windows-native crash, teardown failure, or script-only behavior. Sources: python/cuda_compute/tests/test_examples.py:119, python/cuda_compute/tests/test_examples.py:125, python/cuda_compute/tests/test_examples.py:150. Suggested next steps: First include the return code, stdout, and stderr in the raised failure, then rerun only the select-basic example on Windows without xdist. Use the resulting exit status to determine whether the example or its subprocess lifecycle needs correction. Copy this prompt into a coding agentJobs: 6. Clang 21 tidy checks are promoted to errors · 1 jobExplanation: The clang-tidy lane reports multiple cudax header diagnostics as errors, beginning with a forwarding-reference constructor that can hide copy and move constructors. Additional modernize diagnostics follow, so the tidy target cannot complete. Evidence: clang-tidy ClangCUDA / [CTK12.9 Clang21 C++17] Build(amd64): sm{75}, step 4 Root cause: The Clang 21 tidy configuration enables checks that diagnose existing cudax declarations, with all warnings treated as errors. The first failure is an unconstrained forwarding-reference constructor in `__not_a_domain`; the log also contains independent modernize findings that must be fixed or deliberately suppressed. Sources: cudax/include/cuda/experimental/__execution/domain.cuh:405. Suggested next steps: Address the complete set of logged tidy diagnostics, starting by constraining or replacing the forwarding-reference constructor without breaking copy/move behavior. Rerun the focused ClangCUDA `all-tidy` target with Clang 21. Copy this prompt into a coding agentJobs: |
| `cuda-compute` provides the `cuda.compute` Python interface to the | ||
| [CUDA Core Compute Libraries](https://nvidia.github.io/cccl/cpp.html#cccl-cpp-libraries). |
There was a problem hiding this comment.
This isn't true - I'd just use the verbiage in https://nvidia.github.io/cccl/unstable/python/compute/index.html#cuda-compute-parallel-computing-primitives.
| | `cuda-compute` | `cuda.compute` | Device-level algorithms and iterators | | ||
| | `cccl-headers` | `cuda.cccl.headers` | CCCL headers and CMake package files | | ||
| | `cuda-cccl` | None | Aggregate metapackage | | ||
|
|
There was a problem hiding this comment.
| iterators. |
I'd honestly just get rid of this part. One could consider it an implementation detail that cuda.compute depends on cccl-headers. cuda-cccl (the new metapackage) can explain what it is in its README, but cuda-compute (the downstream package) need not do that.
| The `cuda-cccl` metapackage forwards the same extras, so existing aggregate | ||
| installs such as `pip install cuda-cccl[cu13]` continue to install this package. | ||
|
|
||
| For a header-only installation, install `cccl-headers` directly and use its | ||
| public entry point: | ||
|
|
||
| ```bash | ||
| pip install cccl-headers | ||
| ``` | ||
|
|
||
| ```python | ||
| from cuda.cccl.headers import get_include_paths | ||
|
|
||
| include_paths = get_include_paths() | ||
| ``` |
There was a problem hiding this comment.
Let's preferably leave this out from the README.
| When developing from a source checkout, install the sibling header project | ||
| before this project so the exact dependency is resolved locally: |
There was a problem hiding this comment.
Hmm - is there any way to express this kind of "local" dependency for editable installs? It's a bit sad that we need two install commands
Description
cuda.computeand the upcomingcuda.cooppackage both need the CCCL header payload. Keeping those headers inside the monolithiccuda-cccldistribution makes that payload hard to share without overlapping wheel ownership.This PR assigns each payload to one distribution:
cccl-headerscuda.cccl.headersand the existingcuda.ccclconvenience exportscuda-pathfinder>=1.2.3.cuda-computecuda.computeAPIcccl-headers.cuda-ccclcuda-compute. Existing extras forward to the matching compute extras.The split keeps
cuda.cccl.headers, thecuda.ccclroot exports, andcuda.computeintact. It removes the deprecated pre-1.0cuda.cccl.parallel.experimentalalias. Compute-private helpers and serialization version checks now live undercuda.compute, and serialized artifacts identifycuda-compute.Upgrade from the monolithic wheel
Warning
Before installing this split for the first time, uninstall an older monolithic
cuda-ccclwheel:The old wheel's RECORD owns paths that now belong to
cccl-headersandcuda-compute. A direct in-place upgrade can let pip uninstall the old distribution after installing the new owners, deleting their files. Clean installs and later upgrades within the three-wheel layout do not have this problem.CUDAX CMake package
The packaged CUDAX config keeps
_cudax_cudaxas a non-imported implementation target and exposes it through the global importedcudax::cudaxtarget. This keeps wheel-provided headers out of the compiler's system-header search order and allows downstreaminstall(EXPORT)sets to preservecudax::cudaxas an external dependency.The target requires C++17, adds CUDA 17 when CUDA is enabled, and enables libcudacxx group features. Tests cover C++-only discovery, enabling CUDA after the first discovery, real CUDA compilation, repeated discovery,
CCCL COMPONENTS cudax, and a fresh consumer of an installed downstream export.HostJIT and release integration
The compute wheel owns generated Clang and minimal-CUDA HostJIT resources under
cuda.compute, including the v2 library layout introduced by #9583. Wheel builds resolve those resources at runtime; standalone and editable builds retain their compile-time fallback paths.Linux and Windows wheel jobs now build and test all three distributions. Release collection preserves every unique platform
cuda-computewheel, selects universal wheels from one deterministic Linux producer, and compares duplicate universal wheels by logical ZIP contents. The release validator checks coordinated versions, exact base dependencies, wheel tags, payload ownership, required header families, and zero cross-wheel overlap. Publication accepts artifacts only from a completed, successful, manually dispatched Python-wheel workflow in this repository.Local validation
cccl-headers,cuda-cccl, and both v1/v2cuda-computewheels. All passedtwine check, metadata, RECORD, license, native-content, absolute-path, and zero-overlap audits.SOURCE_DATE_EPOCH; both were byte-for-byte reproducible and taggedpy3-none-any.pip check, public imports, header-family discovery, examples, serialization, and v2 HostJIT resource tests passed.git diff --check, and signature verification for all 17 commits.Exact reviewed head:
104733b67c855120da17acffaa23746977b6e2c0.Remaining gates
Windows execution, ARM64, the full Python/CUDA Toolkit matrix, free-threading/TSan, and external CI remain PR gates. Package-name ownership and index publication are separate release gates; this work did not upload wheels or add a CI override.
Checklist