Skip to content

Commit 875f192

Browse files
lijinf2leofang
andauthored
cuda.core: add tests for ObjectCode.from_object (#2193)
* checkpoint first version * add test_binaries folder * add download test binaries in test-wheel-windows.yaml * use device cuda/std/cstddef * replace shutil with find_nvidia_binary_utility(nvcc) * add testing with linker * Update cuda_core/tests/test_module.py Co-authored-by: Leo Fang <leo80042@gmail.com> * add windows nvcc flag required by CCCL cuda/std/cstddef * addressing comment * move imports to top * add c++17 as required by cccl windows * fix test failure due to using init_cuda.arch instead of sm_{init_cuda.arch} * avoid linking kernel_ptx with cubin that triggers nvrtc nvjitlink version mismatch in ci blackwell gpu * try pin lower ctk version in build and specify test gpu sm codes * use ctk_prev build and add arch=all to nvcc --------- Co-authored-by: Leo Fang <leo80042@gmail.com>
1 parent 68778b6 commit 875f192

8 files changed

Lines changed: 167 additions & 1 deletion

File tree

.github/workflows/build-wheel.yml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -462,6 +462,16 @@ jobs:
462462
cuda-version: ${{ inputs.prev-cuda-version }}
463463
cuda-path: "./cuda_toolkit_prev"
464464

465+
- name: Build cuda.core test binaries
466+
run: bash ${{ env.CUDA_CORE_TEST_BINARIES_DIR }}/build_test_binaries.sh
467+
468+
- name: Upload cuda.core test binaries
469+
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
470+
with:
471+
name: ${{ env.CUDA_CORE_ARTIFACT_NAME }}-test-binaries
472+
path: ${{ env.CUDA_CORE_TEST_BINARIES_DIR }}/*.o
473+
if-no-files-found: error
474+
465475
- name: Download cuda.bindings build artifacts from the prior branch
466476
env:
467477
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}

.github/workflows/test-wheel-linux.yml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,19 @@ jobs:
279279
pwd
280280
ls -lahR $CUDA_CORE_CYTHON_TESTS_DIR
281281
282+
- name: Download cuda.core test binaries
283+
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
284+
with:
285+
name: ${{ env.CUDA_CORE_ARTIFACT_NAME }}-test-binaries
286+
path: ${{ env.CUDA_CORE_TEST_BINARIES_DIR }}
287+
run-id: ${{ inputs.run-id || github.run_id }}
288+
github-token: ${{ secrets.GITHUB_TOKEN }}
289+
290+
- name: Display structure of downloaded cuda.core test binaries
291+
run: |
292+
pwd
293+
ls -lahR $CUDA_CORE_TEST_BINARIES_DIR
294+
282295
- name: Set up Python ${{ matrix.PY_VER }}
283296
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
284297
with:

.github/workflows/test-wheel-windows.yml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,19 @@ jobs:
259259
Get-Location
260260
Get-ChildItem -Recurse -Force $env:CUDA_CORE_CYTHON_TESTS_DIR | Select-Object Mode, LastWriteTime, Length, FullName
261261
262+
- name: Download cuda.core test binaries
263+
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
264+
with:
265+
name: ${{ env.CUDA_CORE_ARTIFACT_NAME }}-test-binaries
266+
path: ${{ env.CUDA_CORE_TEST_BINARIES_DIR }}
267+
run-id: ${{ inputs.run-id || github.run_id }}
268+
github-token: ${{ secrets.GITHUB_TOKEN }}
269+
270+
- name: Display structure of downloaded cuda.core test binaries
271+
run: |
272+
Get-Location
273+
Get-ChildItem -Recurse -Force $env:CUDA_CORE_TEST_BINARIES_DIR | Select-Object Mode, LastWriteTime, Length, FullName
274+
262275
- name: Set up Python ${{ matrix.PY_VER }}
263276
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
264277
with:

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@ cache_driver
1919
cache_runtime
2020
cache_nvrtc
2121

22+
# cuda.core test object fixtures built locally / downloaded as CI artifacts
23+
cuda_core/tests/test_binaries/*.o
24+
2225
# CUDA Python specific (auto-generated)
2326
cuda_bindings/cuda/bindings/_bindings/cyruntime.pxd
2427
cuda_bindings/cuda/bindings/_bindings/cyruntime.pyx

ci/tools/env-vars

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ CUDA_CORE_ARTIFACT_BASENAME="cuda-core-python${PYTHON_VERSION_FORMATTED}-${HOST_
3434
echo "CUDA_CORE_ARTIFACT_NAME=${CUDA_CORE_ARTIFACT_BASENAME}-${SHA}"
3535
echo "CUDA_CORE_ARTIFACTS_DIR=$(realpath "${REPO_DIR}/cuda_core/dist")"
3636
echo "CUDA_CORE_CYTHON_TESTS_DIR=$(realpath "${REPO_DIR}/cuda_core/tests/cython")"
37+
echo "CUDA_CORE_TEST_BINARIES_DIR=$(realpath "${REPO_DIR}/cuda_core/tests/test_binaries")"
3738
echo "PYTHON_VERSION_FORMATTED=${PYTHON_VERSION_FORMATTED}"
3839
} >> $GITHUB_ENV
3940

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
#!/bin/bash
2+
3+
# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
4+
# SPDX-License-Identifier: Apache-2.0
5+
6+
set -euo pipefail
7+
8+
# Build .o test fixtures. Invoked at CI build stage
9+
10+
SCRIPTPATH=$(dirname "$(realpath "$0")")
11+
12+
NVCC_EXTRA_FLAGS=(-std=c++17)
13+
if [[ "${OS:-}" == "Windows_NT" ]]; then
14+
NVCC_EXTRA_FLAGS+=(-Xcompiler /Zc:preprocessor)
15+
fi
16+
17+
nvcc -dc "${NVCC_EXTRA_FLAGS[@]}" -arch=all-major \
18+
-o "${SCRIPTPATH}/saxpy.o" "${SCRIPTPATH}/saxpy.cu"
19+
20+
ls -lah "${SCRIPTPATH}/saxpy.o"
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
#include <cuda/std/cstddef>
5+
6+
__device__ float saxpy_step(float a, float x, float y) {
7+
return a * x + y;
8+
}
9+
10+
template<typename T>
11+
__global__ void saxpy(const T a, const T* x, const T* y, T* out, size_t N) {
12+
const unsigned int tid = threadIdx.x + blockIdx.x * blockDim.x;
13+
for (size_t i = tid; i < N; i += gridDim.x * blockDim.x) {
14+
out[tid] = a * x[tid] + y[tid];
15+
}
16+
}

cuda_core/tests/test_module.py

Lines changed: 91 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,21 @@
22
# SPDX-License-Identifier: Apache-2.0
33

44
import ctypes
5+
import os
56
import pickle
7+
import subprocess
68
import warnings
9+
from pathlib import Path
710

11+
import numpy as np
812
import pytest
913

1014
import cuda.core
11-
from cuda.core import Device, Kernel, ObjectCode, Program, ProgramOptions
15+
from cuda.core import Device, Kernel, Linker, LinkerOptions, ObjectCode, Program, ProgramOptions
1216
from cuda.core._program import _can_load_generated_ptx
1317
from cuda.core._utils.cuda_utils import CUDAError, driver, handle_return
1418
from cuda.core._utils.version import binding_version, driver_version
19+
from cuda.pathfinder import find_nvidia_binary_utility
1520

1621
try:
1722
import numba
@@ -172,6 +177,32 @@ def get_saxpy_fatbin(init_cuda):
172177
return bytes(fatbin), sym_map
173178

174179

180+
@pytest.fixture(scope="module")
181+
def get_saxpy_object():
182+
"""Read the pre-built saxpy.o.
183+
184+
In CI: produced by build stage into a test wheel file.
185+
In local dev: auto-built on demand if nvcc is available; if you edit
186+
saxpy.cu, remove the stale saxpy.o to force a rebuild.
187+
"""
188+
binaries_dir = Path(__file__).parent / "test_binaries"
189+
obj_path = binaries_dir / "saxpy.o"
190+
191+
if not obj_path.is_file():
192+
if find_nvidia_binary_utility("nvcc") is None:
193+
pytest.skip(
194+
f"saxpy.o not found at {obj_path} and nvcc is unavailable. "
195+
"In CI this is downloaded from the build stage."
196+
)
197+
subprocess.run( # noqa: S603
198+
["bash", str(binaries_dir / "build_test_binaries.sh")], # noqa: S607
199+
check=True,
200+
env=os.environ,
201+
)
202+
203+
return obj_path.read_bytes()
204+
205+
175206
def test_get_kernel(init_cuda):
176207
kernel = """extern "C" __global__ void ABC() { }"""
177208

@@ -330,6 +361,65 @@ def test_object_code_load_fatbin_from_file(get_saxpy_fatbin, tmp_path, convert_p
330361
mod_obj.get_kernel("saxpy<double>") # force loading
331362

332363

364+
def test_object_code_load_object(get_saxpy_object):
365+
obj = get_saxpy_object
366+
assert isinstance(obj, bytes)
367+
mod_obj = ObjectCode.from_object(obj)
368+
assert mod_obj.code == obj
369+
assert mod_obj.code_type == "object"
370+
with pytest.raises(RuntimeError, match=r'Unsupported code type "object"'):
371+
mod_obj.get_kernel("saxpy<float>")
372+
373+
374+
def test_object_code_load_object_from_file(get_saxpy_object, tmp_path):
375+
obj_file = tmp_path / "test.o"
376+
obj_file.write_bytes(get_saxpy_object)
377+
arg = str(obj_file)
378+
mod_obj = ObjectCode.from_object(arg)
379+
assert mod_obj.code == arg
380+
assert mod_obj.code_type == "object"
381+
382+
383+
def test_object_code_load_object_with_linker(get_saxpy_object, init_cuda):
384+
arch = f"sm_{init_cuda.arch}"
385+
kernel_code = Program(
386+
r"""
387+
extern __device__ float saxpy_step(float a, float x, float y);
388+
extern "C" __global__ void linked_kernel(float a, float x, float y, float* out) {
389+
if (threadIdx.x == 0 && blockIdx.x == 0) *out = saxpy_step(a, x, y);
390+
}
391+
""",
392+
"c++",
393+
ProgramOptions(relocatable_device_code=True, arch=arch),
394+
).compile("cubin")
395+
linked = Linker(
396+
kernel_code,
397+
ObjectCode.from_object(get_saxpy_object),
398+
options=LinkerOptions(arch=arch),
399+
).link("cubin")
400+
kernel = linked.get_kernel("linked_kernel")
401+
402+
stream = init_cuda.create_stream()
403+
host_buf = cuda.core.LegacyPinnedMemoryResource().allocate(4)
404+
result = np.from_dlpack(host_buf).view(np.float32)
405+
result[:] = 0.0
406+
dev_buf = init_cuda.memory_resource.allocate(4, stream=init_cuda.default_stream)
407+
408+
cuda.core.launch(
409+
stream,
410+
cuda.core.LaunchConfig(grid=1, block=1),
411+
kernel,
412+
np.float32(2.0),
413+
np.float32(3.0),
414+
np.float32(4.0),
415+
dev_buf,
416+
)
417+
dev_buf.copy_to(host_buf, stream=stream)
418+
stream.sync()
419+
420+
assert result[0] == 10.0
421+
422+
333423
def test_saxpy_arguments(get_saxpy_kernel_cubin, cuda12_4_prerequisite_check):
334424
krn, _ = get_saxpy_kernel_cubin
335425

0 commit comments

Comments
 (0)