feat(cccl): integrate missing CCCL directories — python/, ci/, .agent/, docs/, test/
Sparse-checkout from NVIDIA/cccl main branch to complete cccl_upstream: Added: - python/cuda_cccl/ (226 files) — Python bindings for device-level algorithms Critical for muh toolchain: cuda.compute.reduce_into, scan, radix_sort, etc. Includes 204 .py files with full test coverage for all 27 algorithms - ci/ (163 files) — Build/test infrastructure build_cub.sh, test_cub.sh, build_and_test_targets.sh, matrix.yaml Directly maps to our [INFRA-CI] and [INFRA-BUILD] items - .agent/skills/ (7 files) — NVIDIA's own agent skills for CCCL cccl-style/SKILL.md, cccl-test/SKILL.md, sass-diff/SKILL.md - docs/ (491 files) — Official CCCL documentation CI references, CMake guides, Python compute docs, libcudacxx PTX docs - test/ (12 files) — Top-level integration tests (cuda_smoke, stdpar) - Root configs: .clang-format, .clang-tidy, CONTRIBUTING.md, pyproject.toml - CLAUDE.md symlink → AGENTS.md (NVIDIA's standard) cccl_upstream now mirrors full NVIDIA/cccl structure: Before: 42M (cub + thrust + libcudacxx + cudax + c + examples + benchmarks) After: 53M (+python +ci +docs +.agent +test +configs) This completes the CCCL base needed for: - [muh-bench] items: ci/util/build_and_test_targets.sh for targeted builds - [CCCL-verify] items: python/cuda_cccl/tests/ as reference implementations - [CCCL-test] items: ci/test_cub.sh, ci/test_thrust.sh - Agent workflow: .agent/skills/ for consistent style and test patterns
This commit is contained in:
169
cccl_upstream/python/cuda_cccl/tests/compute/conftest.py
Normal file
169
cccl_upstream/python/cuda_cccl/tests/compute/conftest.py
Normal file
@@ -0,0 +1,169 @@
|
||||
import builtins
|
||||
from collections.abc import Generator
|
||||
|
||||
import numpy as np
|
||||
import pytest
|
||||
|
||||
from cuda.core import Device, Stream
|
||||
|
||||
try:
|
||||
from cuda.compute._build_info import USING_V2
|
||||
except ImportError:
|
||||
USING_V2 = False
|
||||
|
||||
check_ldl_stl_in_sass = False
|
||||
|
||||
|
||||
# Define a pytest fixture that returns random arrays with different dtypes
|
||||
@pytest.fixture(
|
||||
params=[
|
||||
np.int8,
|
||||
np.int16,
|
||||
np.int32,
|
||||
np.int64,
|
||||
np.uint8,
|
||||
np.uint16,
|
||||
np.uint32,
|
||||
np.uint64,
|
||||
np.float16,
|
||||
np.float32,
|
||||
np.float64,
|
||||
np.complex64,
|
||||
np.complex128,
|
||||
]
|
||||
)
|
||||
def input_array(request):
|
||||
dtype = np.dtype(request.param)
|
||||
sample_size = 1000
|
||||
|
||||
# Generate random values based on the dtype
|
||||
if np.issubdtype(dtype, np.integer):
|
||||
is_unsigned = dtype.kind == "u"
|
||||
# For integer types, use np.random.randint for random integers
|
||||
if is_unsigned:
|
||||
low_inclusive, high_exclusive = 0, 8
|
||||
else:
|
||||
low_inclusive, high_exclusive = -5, 6
|
||||
array = np.random.randint(
|
||||
low=low_inclusive, high=high_exclusive, size=sample_size, dtype=dtype
|
||||
)
|
||||
elif np.issubdtype(dtype, np.floating):
|
||||
# For floating-point types, use np.random.random and cast to the required dtype
|
||||
array = np.random.random(sample_size).astype(dtype)
|
||||
elif np.issubdtype(dtype, np.complexfloating):
|
||||
# For complex types, generate random real and imaginary parts
|
||||
packed = np.random.random(2 * sample_size)
|
||||
real_part = packed[:sample_size]
|
||||
imag_part = packed[sample_size:]
|
||||
array = (real_part + 1j * imag_part).astype(dtype)
|
||||
|
||||
return array
|
||||
|
||||
|
||||
# Define a pytest fixture that returns random floating-point arrays only
|
||||
@pytest.fixture(
|
||||
params=[
|
||||
np.float32,
|
||||
np.float64,
|
||||
]
|
||||
)
|
||||
def floating_array(request):
|
||||
dtype = np.dtype(request.param)
|
||||
sample_size = 1000
|
||||
|
||||
# Generate random floating-point values
|
||||
array = np.random.random(sample_size).astype(dtype)
|
||||
return array
|
||||
|
||||
|
||||
@pytest.fixture(scope="function")
|
||||
def cuda_stream() -> Generator[Stream, None, None]:
|
||||
device = Device()
|
||||
device.set_current()
|
||||
stream = device.create_stream()
|
||||
try:
|
||||
yield stream
|
||||
finally:
|
||||
stream.close()
|
||||
|
||||
|
||||
@pytest.fixture(scope="function", autouse=True)
|
||||
def verify_sass(request):
|
||||
if request.node.get_closest_marker("no_verify_sass"):
|
||||
return
|
||||
|
||||
if not check_ldl_stl_in_sass:
|
||||
return
|
||||
|
||||
# Pull monkeypatch dynamically rather than as a fixture parameter so this
|
||||
# autouse fixture does not add monkeypatch to every test's static fixture
|
||||
# closure. pytest-run-parallel treats monkeypatch as thread-unsafe based on
|
||||
# that closure, so a parameter here would serialize the entire free-threaded
|
||||
# parallel sweep -- even though this fixture only patches on the opt-in
|
||||
# SASS-check path (check_ldl_stl_in_sass, off by default and in CI).
|
||||
monkeypatch = request.getfixturevalue("monkeypatch")
|
||||
|
||||
import cuda.compute._cccl_interop
|
||||
|
||||
monkeypatch.setattr(
|
||||
cuda.compute._cccl_interop,
|
||||
"_check_sass",
|
||||
True,
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def raise_on_numba_import(monkeypatch):
|
||||
"""This fixture will raise if a test attempts to import numba"""
|
||||
real_import = builtins.__import__
|
||||
|
||||
def guarded_import(name, *args, **kwargs):
|
||||
if name == "numba" or name.startswith("numba."):
|
||||
raise ModuleNotFoundError(
|
||||
"This test is marked 'no_numba' but attempted to import it"
|
||||
)
|
||||
return real_import(name, *args, **kwargs)
|
||||
|
||||
monkeypatch.setattr(builtins, "__import__", guarded_import)
|
||||
|
||||
|
||||
def pytest_collection_modifyitems(config, items):
|
||||
"""Runs after pytest collects the tests. Makes a test marked no_numba fail
|
||||
if it imports numba, and skips a test marked serialization when running on
|
||||
the v2 (HostJIT) backend."""
|
||||
serialization_skip = pytest.mark.skip(
|
||||
reason="serialization not supported on v2 (HostJIT) backend"
|
||||
)
|
||||
|
||||
# Tests marked no_numba must not import numba. We enforce that by attaching
|
||||
# the raise_on_numba_import fixture defined above to each one; it raises if
|
||||
# numba is imported.
|
||||
#
|
||||
# We skip attaching it during a real pytest-run-parallel sweep of more than
|
||||
# one thread: the fixture uses monkeypatch, which pytest-run-parallel
|
||||
# serializes as thread-unsafe, so attaching it to every no_numba test would
|
||||
# make the whole sweep run serially and defeat its purpose. A single-threaded
|
||||
# run has no sweep to protect, so we attach it and keep the check.
|
||||
#
|
||||
# config.getoption gives the --parallel-threads value as an int for the
|
||||
# default but a str when passed on the command line; normalize to str and
|
||||
# count the run as parallel only for an explicit number > 1:
|
||||
#
|
||||
# pytest ... -> 1 single-threaded, attach
|
||||
# pytest --parallel-threads=1 ... -> "1" single-threaded, attach
|
||||
# pytest --parallel-threads=8 ... -> "8" parallel, skip (CI sweep)
|
||||
# pytest --parallel-threads=auto ... -> "auto" single-threaded, attach
|
||||
#
|
||||
# "auto" counts as single-threaded on purpose: it can resolve to one CPU, so
|
||||
# keeping the check is safer than dropping it on a non-parallel run. isdigit
|
||||
# also stops int() from raising on the non-numeric "auto".
|
||||
parallel_threads = str(config.getoption("parallel_threads", 1))
|
||||
running_parallel = parallel_threads.isdigit() and int(parallel_threads) > 1
|
||||
for item in items:
|
||||
# no_numba: add raise_on_numba_import unless we skip it for the sweep
|
||||
if item.get_closest_marker("no_numba") and not running_parallel:
|
||||
if "raise_on_numba_import" not in item.fixturenames:
|
||||
item.fixturenames.append("raise_on_numba_import")
|
||||
# serialization is unsupported on v2 (HostJIT); skip those tests there
|
||||
if USING_V2 and item.get_closest_marker("serialization"):
|
||||
item.add_marker(serialization_skip)
|
||||
Reference in New Issue
Block a user