-
Notifications
You must be signed in to change notification settings - Fork 328
Add CUDA version compatibility check #1412
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 8 commits
d999f40
62dfcca
73611ed
b2083ed
fdb3a7e
3a5c210
424a113
6071609
7925693
7f23b08
4f20ae0
a368e0f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,56 @@ | ||
| # SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. | ||
| # SPDX-License-Identifier: LicenseRef-NVIDIA-SOFTWARE-LICENSE | ||
|
|
||
| import os | ||
| import warnings | ||
|
|
||
| # Track whether we've already checked major version compatibility | ||
| _major_version_compatibility_checked = False | ||
|
|
||
|
|
||
| def warn_if_cuda_major_version_mismatch(): | ||
| """Warn if the CUDA driver major version is older than cuda-bindings compile-time version. | ||
|
|
||
| This function compares the CUDA major version that cuda-bindings was compiled | ||
| against with the CUDA major version supported by the installed driver. If the | ||
| compile-time major version is greater than the driver's major version, a warning | ||
| is issued. | ||
|
|
||
| The check runs only once per process. Subsequent calls are no-ops. | ||
|
|
||
| The warning can be suppressed by setting the environment variable | ||
| ``CUDA_PYTHON_DISABLE_MAJOR_VERSION_WARNING=1``. | ||
| """ | ||
| global _major_version_compatibility_checked | ||
| if _major_version_compatibility_checked: | ||
| return | ||
| _major_version_compatibility_checked = True | ||
|
|
||
| # Allow users to suppress the warning | ||
| if os.environ.get("CUDA_PYTHON_DISABLE_MAJOR_VERSION_WARNING"): | ||
| return | ||
|
|
||
| # Import here to avoid circular imports and allow lazy loading | ||
| from cuda.bindings import driver | ||
|
|
||
| # Get compile-time CUDA version from cuda-bindings | ||
| compile_version = driver.CUDA_VERSION # e.g., 13010 | ||
| compile_major = compile_version // 1000 | ||
|
|
||
| # Get runtime driver version | ||
| err, runtime_version = driver.cuDriverGetVersion() | ||
| if err != driver.CUresult.CUDA_SUCCESS: | ||
| raise RuntimeError(f"Failed to query CUDA driver version: {err}") | ||
|
|
||
| runtime_major = runtime_version // 1000 | ||
|
|
||
| if compile_major > runtime_major: | ||
| warnings.warn( | ||
| f"cuda-bindings was built for CUDA major version {compile_major}, but the " | ||
| f"NVIDIA driver only supports up to CUDA {runtime_major}. Some cuda-bindings " | ||
| f"features may not work correctly. Consider updating your NVIDIA driver, " | ||
| f"or using a cuda-bindings version built for CUDA {runtime_major}. " | ||
| f"(Set CUDA_PYTHON_DISABLE_MAJOR_VERSION_WARNING=1 to suppress this warning.)", | ||
| UserWarning, | ||
| stacklevel=3, | ||
| ) | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -9,6 +9,8 @@ Runtime Environment Variables | |
|
|
||
| - ``CUDA_PYTHON_CUDA_PER_THREAD_DEFAULT_STREAM`` : When set to 1, the default stream is the per-thread default stream. When set to 0, the default stream is the legacy default stream. This defaults to 0, for the legacy default stream. See `Stream Synchronization Behavior <https://docs.nvidia.com/cuda/cuda-runtime-api/stream-sync-behavior.html>`_ for an explanation of the legacy and per-thread default streams. | ||
|
|
||
| - ``CUDA_PYTHON_DISABLE_MAJOR_VERSION_WARNING`` : When set to 1, suppresses warnings about CUDA major version mismatches between ``cuda-bindings`` and the installed driver. | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. TBH I am not sure if this env var is really useful, because the added API is entirely opt-in?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The version check can be triggered indirectly from the end user's POV. For example, by using cuda-core. Without this, each library that calls the version-check function would need its own opt-out, and users might need to set them all. To me it makes sense to place the opt-out alongside the implementation and there is little/no cost.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I insist that this is useless because this is listed in the cuda-bindings docs, not cuda-core. But cuda-bindings does not call this function anywhere, whereas cuda-core users don't necessarily look at cuda-bindings docs. Can we please create a new
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. xref: #1412 (comment)
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Even better: Document this env var in both docs.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good point. The same argument applies to all cuda.bindings runtime variables, so I:
|
||
|
|
||
|
|
||
| Build-Time Environment Variables | ||
| -------------------------------- | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,98 @@ | ||
| # SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. | ||
| # SPDX-License-Identifier: LicenseRef-NVIDIA-SOFTWARE-LICENSE | ||
|
|
||
| import os | ||
| import warnings | ||
| from unittest import mock | ||
|
|
||
| import pytest | ||
| from cuda.bindings import driver | ||
| from cuda.bindings.utils import _version_check, warn_if_cuda_major_version_mismatch | ||
|
|
||
|
|
||
| class TestVersionCompatibilityCheck: | ||
| """Tests for CUDA major version mismatch warning function.""" | ||
|
|
||
| def setup_method(self): | ||
| """Reset the version compatibility check flag before each test.""" | ||
| _version_check._major_version_compatibility_checked = False | ||
|
leofang marked this conversation as resolved.
Outdated
|
||
|
|
||
| def teardown_method(self): | ||
| """Reset the version compatibility check flag after each test.""" | ||
| _version_check._major_version_compatibility_checked = False | ||
|
|
||
| def test_no_warning_when_driver_newer(self): | ||
| """No warning should be issued when driver version >= compile version.""" | ||
| # Mock compile version 12.9 and driver version 13.0 | ||
| with ( | ||
| mock.patch.object(driver, "CUDA_VERSION", 12090), | ||
| mock.patch.object(driver, "cuDriverGetVersion", return_value=(driver.CUresult.CUDA_SUCCESS, 13000)), | ||
| warnings.catch_warnings(record=True) as w, | ||
| ): | ||
| warnings.simplefilter("always") | ||
| warn_if_cuda_major_version_mismatch() | ||
| assert len(w) == 0 | ||
|
|
||
| def test_no_warning_when_same_major_version(self): | ||
| """No warning should be issued when major versions match.""" | ||
| # Mock compile version 12.9 and driver version 12.8 | ||
| with ( | ||
| mock.patch.object(driver, "CUDA_VERSION", 12090), | ||
| mock.patch.object(driver, "cuDriverGetVersion", return_value=(driver.CUresult.CUDA_SUCCESS, 12080)), | ||
| warnings.catch_warnings(record=True) as w, | ||
| ): | ||
| warnings.simplefilter("always") | ||
| warn_if_cuda_major_version_mismatch() | ||
| assert len(w) == 0 | ||
|
|
||
| def test_warning_when_compile_major_newer(self): | ||
| """Warning should be issued when compile major version > driver major version.""" | ||
| # Mock compile version 13.0 and driver version 12.8 | ||
| with ( | ||
| mock.patch.object(driver, "CUDA_VERSION", 13000), | ||
| mock.patch.object(driver, "cuDriverGetVersion", return_value=(driver.CUresult.CUDA_SUCCESS, 12080)), | ||
| warnings.catch_warnings(record=True) as w, | ||
| ): | ||
| warnings.simplefilter("always") | ||
| warn_if_cuda_major_version_mismatch() | ||
| assert len(w) == 1 | ||
| assert issubclass(w[0].category, UserWarning) | ||
| assert "cuda-bindings was built for CUDA major version 13" in str(w[0].message) | ||
| assert "only supports up to CUDA 12" in str(w[0].message) | ||
|
|
||
| def test_warning_only_issued_once(self): | ||
| """Warning should only be issued once per process.""" | ||
| with ( | ||
| mock.patch.object(driver, "CUDA_VERSION", 13000), | ||
| mock.patch.object(driver, "cuDriverGetVersion", return_value=(driver.CUresult.CUDA_SUCCESS, 12080)), | ||
| warnings.catch_warnings(record=True) as w, | ||
| ): | ||
| warnings.simplefilter("always") | ||
| warn_if_cuda_major_version_mismatch() | ||
| warn_if_cuda_major_version_mismatch() | ||
| warn_if_cuda_major_version_mismatch() | ||
| # Only one warning despite multiple calls | ||
| assert len(w) == 1 | ||
|
|
||
| def test_warning_suppressed_by_env_var(self): | ||
| """Warning should be suppressed when CUDA_PYTHON_DISABLE_MAJOR_VERSION_WARNING is set.""" | ||
| with ( | ||
| mock.patch.object(driver, "CUDA_VERSION", 13000), | ||
| mock.patch.object(driver, "cuDriverGetVersion", return_value=(driver.CUresult.CUDA_SUCCESS, 12080)), | ||
| mock.patch.dict(os.environ, {"CUDA_PYTHON_DISABLE_MAJOR_VERSION_WARNING": "1"}), | ||
| warnings.catch_warnings(record=True) as w, | ||
| ): | ||
| warnings.simplefilter("always") | ||
| warn_if_cuda_major_version_mismatch() | ||
| assert len(w) == 0 | ||
|
|
||
| def test_error_when_driver_version_fails(self): | ||
| """Should raise RuntimeError if cuDriverGetVersion fails.""" | ||
| with ( | ||
| mock.patch.object(driver, "CUDA_VERSION", 13000), | ||
| mock.patch.object( | ||
| driver, "cuDriverGetVersion", return_value=(driver.CUresult.CUDA_ERROR_NOT_INITIALIZED, 0) | ||
| ), | ||
| pytest.raises(RuntimeError, match="Failed to query CUDA driver version"), | ||
| ): | ||
| warn_if_cuda_major_version_mismatch() | ||
Uh oh!
There was an error while loading. Please reload this page.