Skip to content

Commit f2eb9ab

Browse files
committed
test(core.utils): e2e real NVRTC compile through persistent caches
Parametrized over SQLiteProgramCache and FileStreamProgramCache: compile a trivial CUDA C++ kernel with Program.compile("cubin"), store the ObjectCode under a key produced by make_program_cache_key, close and reopen the cache handle, retrieve the entry, and confirm the deserialised ObjectCode still yields a working Kernel via get_kernel. Covers the full user flow (compile → persist → reopen → driver load) that the existing fake-ObjectCode tests do not exercise.
1 parent d0c9dd0 commit f2eb9ab

File tree

1 file changed

+70
-0
lines changed

1 file changed

+70
-0
lines changed

cuda_core/tests/test_program_cache.py

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -584,3 +584,73 @@ def test_filestream_cache_unbounded_by_default(tmp_path):
584584
for i in range(20):
585585
cache[f"k{i}".encode()] = _fake_object_code(b"X" * 1024, name=f"n{i}")
586586
assert len(cache) == 20
587+
588+
589+
# ---------------------------------------------------------------------------
590+
# End-to-end: real NVRTC compilation through persistent cache
591+
# ---------------------------------------------------------------------------
592+
593+
594+
@pytest.mark.parametrize(
595+
"backend",
596+
[
597+
pytest.param("sqlite", marks=needs_sqlite3),
598+
pytest.param("filestream"),
599+
],
600+
)
601+
def test_cache_roundtrip_with_real_compilation(backend, tmp_path, init_cuda):
602+
"""Compile a real kernel, persist it, reopen the cache, and reload the kernel.
603+
604+
Exercises the full user workflow: NVRTC compile → persistent store → fresh
605+
process (simulated by closing and reopening the cache handle) → driver-side
606+
module load from the deserialised ``ObjectCode``.
607+
"""
608+
from cuda.core import Program, ProgramOptions
609+
from cuda.core._module import Kernel
610+
from cuda.core.utils import (
611+
FileStreamProgramCache,
612+
SQLiteProgramCache,
613+
make_program_cache_key,
614+
)
615+
616+
code = 'extern "C" __global__ void my_kernel() {}'
617+
code_type = "c++"
618+
target_type = "cubin"
619+
options = ProgramOptions(name="cached_kernel")
620+
621+
program = Program(code, code_type, options=options)
622+
try:
623+
compiled = program.compile(target_type)
624+
finally:
625+
program.close()
626+
assert isinstance(compiled.get_kernel("my_kernel"), Kernel)
627+
628+
key = make_program_cache_key(
629+
code=code,
630+
code_type=code_type,
631+
options=options,
632+
target_type=target_type,
633+
)
634+
635+
if backend == "sqlite":
636+
path = tmp_path / "cache.db"
637+
cache_cls = SQLiteProgramCache
638+
else:
639+
path = tmp_path / "fc"
640+
cache_cls = FileStreamProgramCache
641+
642+
# First "process": compile and store.
643+
with cache_cls(path) as cache:
644+
assert key not in cache
645+
cache[key] = compiled
646+
647+
# Second "process": reopen a fresh handle and retrieve.
648+
with cache_cls(path) as cache:
649+
assert key in cache
650+
cached = cache[key]
651+
652+
assert cached.code_type == target_type
653+
assert cached.name == "cached_kernel"
654+
assert bytes(cached.code) == bytes(compiled.code)
655+
# The deserialised ObjectCode must still be usable against the driver.
656+
assert isinstance(cached.get_kernel("my_kernel"), Kernel)

0 commit comments

Comments
 (0)