Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion cuda_core/cuda/core/system/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
__all__ = [
"CUDA_BINDINGS_NVML_IS_COMPATIBLE",
"get_driver_version",
"get_driver_version_full",
"get_num_devices",
"get_process_name",
]
Expand Down
61 changes: 23 additions & 38 deletions cuda_core/cuda/core/system/_system.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -26,55 +26,41 @@ if CUDA_BINDINGS_NVML_IS_COMPATIBLE:

from cuda.core.system._nvml_context import initialize
else:
from cuda.core._utils.cuda_utils import driver, handle_return, runtime
from cuda.core._utils.cuda_utils import handle_return, runtime


def get_driver_version(kernel_mode: bool = False) -> tuple[int, int]:
def get_driver_version() -> tuple[tuple[int, ...], tuple[int, ...]]:
"""
Get the driver version.

Parameters
----------
kernel_mode: bool
When `True`, return the kernel-mode driver version, e.g. 580.65.06.
Otherwise, return the user-mode driver version, e.g. 13.0.1.
Returns both the user-mode (UMD / CUDA) driver version and the
kernel-mode (KMD / GPU) driver version.

Returns
-------
version: tuple[int, int]
Tuple in the format `(MAJOR, MINOR)`.
version : tuple[tuple[int, ...], tuple[int, ...]]
``(umd_version, kmd_version)`` where ``umd_version`` is typically
a 2-tuple ``(MAJOR, MINOR)`` and ``kmd_version`` is typically
a 3-tuple ``(MAJOR, MINOR, PATCH)`` (2-tuple on WSL).

Raises
------
RuntimeError
If the NVML library is not available.
"""
return get_driver_version_full(kernel_mode)[:2]
if not CUDA_BINDINGS_NVML_IS_COMPATIBLE:
raise RuntimeError("get_driver_version requires NVML support")
initialize()

# UMD (user-mode / CUDA toolkit) version
cdef int v
v = nvml.system_get_cuda_driver_version()
umd = (v // 1000, (v // 10) % 100)

def get_driver_version_full(kernel_mode: bool = False) -> tuple[int, int, int]:
"""
Get the full driver version.
# KMD (kernel-mode / GPU driver) version
kmd = tuple(int(x) for x in nvml.system_get_driver_version().split("."))

Parameters
----------
kernel_mode: bool
When `True`, return the kernel-mode driver version, e.g. 580.65.06.
Otherwise, return the user-mode driver version, e.g. 13.0.1.

Returns
-------
version: tuple[int, int, int]
Tuple in the format `(MAJOR, MINOR, PATCH)`.
"""
cdef int v
if kernel_mode:
if not CUDA_BINDINGS_NVML_IS_COMPATIBLE:
raise ValueError("Kernel-mode driver version requires NVML support")
initialize()
return tuple(int(v) for v in nvml.system_get_driver_version().split("."))
else:
if CUDA_BINDINGS_NVML_IS_COMPATIBLE:
initialize()
v = nvml.system_get_cuda_driver_version()
else:
v = handle_return(driver.cuDriverGetVersion())
return (v // 1000, (v // 10) % 100, v % 10)
return (umd, kmd)


def get_nvml_version() -> tuple[int, ...]:
Expand Down Expand Up @@ -138,7 +124,6 @@ def get_process_name(pid: int) -> str:
__all__ = [
"get_driver_branch",
"get_driver_version",
"get_driver_version_full",
"get_nvml_version",
"get_num_devices",
"get_process_name",
Expand Down
1 change: 0 additions & 1 deletion cuda_core/docs/source/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -255,7 +255,6 @@ Basic functions
:toctree: generated/

system.get_driver_version
system.get_driver_version_full
system.get_driver_branch
system.get_num_devices
system.get_nvml_version
Expand Down
8 changes: 8 additions & 0 deletions cuda_core/docs/source/release/1.0.0-notes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,14 @@ Breaking changes
:mod:`cuda.core.utils` module.
(`#2028 <https://github.com/NVIDIA/cuda-python/issues/2028>`__)

- Consolidated ``system.get_driver_version()`` and
``system.get_driver_version_full()`` into a single
:func:`system.get_driver_version` that returns
``(umd_version, kmd_version)`` — a 2-tuple of version tuples
(UMD is ``(MAJOR, MINOR)``, KMD is ``(MAJOR, MINOR, PATCH)``).
The function now requires NVML support and raises :class:`RuntimeError`
if it is not available.

Fixes and enhancements
-----------------------

Expand Down
47 changes: 22 additions & 25 deletions cuda_core/tests/system/test_system_system.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,25 @@
from .conftest import skip_if_nvml_unsupported


@skip_if_nvml_unsupported
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why does nvml have to be supported to check the driver version?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because this PR now uses NVML API calls exclusively.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I'd argue there should be more traces to the reader to make that obvious. That coupled with the incorrect header at the top of the file make it difficult to parse.

def test_driver_version():
driver_version = system.get_driver_version()
umd, kmd = system.get_driver_version()

# UMD: 2-tuple (major, minor), cross-check against cuDriverGetVersion
assert isinstance(umd, tuple)
assert len(umd) == 2
version = handle_return(driver.cuDriverGetVersion())
expected_driver_version = (version // 1000, (version % 1000) // 10)
assert driver_version == expected_driver_version, "Driver version does not match expected value"
expected_umd = (version // 1000, (version % 1000) // 10)
assert umd == expected_umd, "UMD driver version does not match expected value"

# KMD: 3-tuple (major, minor, patch), or 2-tuple on WSL
assert isinstance(kmd, tuple)
assert len(kmd) in (2, 3)
ver_maj, ver_min, *ver_patch = kmd
assert 400 <= ver_maj < 1000
assert ver_min >= 0
if ver_patch:
assert 0 <= ver_patch[0] <= 99


def test_num_devices():
Expand All @@ -41,28 +55,11 @@ def test_devices():
assert device.device_id == expected_device.device_id, "Device ID does not match expected value"


def test_cuda_driver_version():
cuda_driver_version = system.get_driver_version_full()
assert isinstance(cuda_driver_version, tuple)
assert len(cuda_driver_version) == 3

ver_maj, ver_min, ver_patch = cuda_driver_version
assert ver_maj >= 10
assert 0 <= ver_min <= 99
assert 0 <= ver_patch <= 9


@skip_if_nvml_unsupported
def test_gpu_driver_version():
driver_version = system.get_driver_version(kernel_mode=True)
assert isinstance(driver_version, tuple)
assert len(driver_version) in (2, 3)

(ver_maj, ver_min, *ver_patch) = driver_version
assert 400 <= ver_maj < 1000
assert ver_min >= 0
if ver_patch:
assert 0 <= ver_patch[0] <= 99
def test_driver_version_requires_nvml():
if system.CUDA_BINDINGS_NVML_IS_COMPATIBLE:
pytest.skip("NVML is available, cannot test the error path")
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

More comments explaining why NVML being available prevents testing would be a good trace for the reader.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because this test needs a negation for @skip_if_nvml_unsupported.

with pytest.raises(RuntimeError, match="requires NVML support"):
system.get_driver_version()


@skip_if_nvml_unsupported
Expand Down