torch.version.cuda reports the CUDA runtime version embedded in your PyTorch installation. It is not the toolkit that nvcc --version finds on your PATH, and it is not the CUDA version in the nvidia-smi header, which is the highest CUDA version your driver supports. For a pip or conda install, the version PyTorch was built with must be no newer than the maximum the installed driver supports.
So the three numbers need not agree. A disagreement becomes a problem when the build is newer than what the driver supports.
Three CUDA numbers on one machine, and what each one belongs to
Inside PyTorch, torch.cuda is the module that sets up and runs CUDA operations. It keeps track of the currently selected GPU, and CUDA tensors you allocate are created on that device by default. The libraries it calls at runtime (libcudart, cuBLAS, cuDNN) come bundled in the wheel rather than from a system-wide CUDA installation.
| Where you look | What it reports | Which layer it describes |
|---|---|---|
torch.version.cuda |
CUDA runtime version the PyTorch binary was built with and bundles; None on a CPU-only build |
The framework build |
nvidia-smi header |
The highest CUDA version the installed driver supports | The kernel driver |
nvcc --version |
The system CUDA toolkit’s compiler version | A separate toolkit install that standard PyTorch wheels do not use |
torch.__version__ suffix (+cu128, +cpu, +rocm…) |
Which accelerator variant of the wheel you installed | The wheel itself |
Newer drivers stay backward compatible with applications built on older toolkits, so a driver’s version sets the ceiling of the CUDA versions it can run, not a floor. The split between the driver and the toolkit, and why both exist, is covered in more depth in CUDA driver versus CUDA toolkit.
Does my system CUDA toolkit have to match the version PyTorch was built with?
No, not for a prebuilt wheel. NVIDIA’s requirement is that a CUDA application needs a CUDA-enabled GPU and a driver compatible with the toolkit used to build that application, plus the right versions of any libraries it links dynamically. A PyTorch wheel brings its own runtime libraries, so the toolkit under /usr/local/cuda only comes into play when you compile PyTorch from source, build custom CUDA extensions, or run other software that depends on system-level CUDA libraries.
The constraint that does bind is between the driver and the CUDA version of the wheel. NVIDIA’s compatibility guide gives three routes:
- Backward compatibility: a newer driver runs applications built with an older toolkit, with no extra requirements.
- Minor version compatibility: from CUDA 11 onwards, an application built with any toolkit in a major release family runs, with a limited feature set, on a driver at or above that family’s minimum. The minimums are 580 or newer for CUDA 13.x, 525 or newer for CUDA 12.x, and 450 or newer for CUDA 11.x.
- Forward compatibility: a
cuda-compat-<major>-<minor>package lets applications built with a newer toolkit run on an older base driver across major families, subject to platform and GPU support. The package is named after the highest toolkit it supports.
Minor version compatibility has limits. Applications that compile device code to PTX will not work on older drivers, and NVIDIA’s advice is to upgrade the driver. Features that span both the toolkit and the driver can fail on an older driver with cudaErrorCallRequiresNewerDriver. The route also depends on compiling for a named target architecture (nvcc -arch=sm_xx), meaning real machine code (SASS) for that GPU rather than PTX.
There is also a packaging change to be aware of. Before CUDA 13.4, every CUDA Toolkit shipped with a matching NVIDIA driver package. From CUDA 13.4 the Linux driver is distributed separately and has to be installed on its own. For how the driver, runtime, toolkit and GPU generation interact as a whole, see the four axes of CUDA compatibility.
Checking the PyTorch CUDA version and GPU visibility in one go
Read the build, the version and visibility together:
import torch
print(torch.__version__) # suffix shows the wheel variant: +cuXXX, +rocmX.Y, +cpu
print(torch.version.cuda) # CUDA runtime the wheel was built with, or None
print(torch.version.hip) # HIP version on ROCm builds, otherwise None
print(torch.backends.cuda.is_built()) # was this binary compiled with CUDA support?
print(torch.cuda.is_available()) # can it actually use a GPU on this machine?
if torch.cuda.is_available():
print(torch.cuda.device_count())
print(torch.cuda.get_device_name(0))
print(torch.cuda.get_device_capability(0))
print(torch.cuda.get_arch_list()) # GPU architectures this wheel ships kernels for
Then compare against the system side with nvidia-smi (driver version and its CUDA ceiling). The system toolkit that nvcc --version reports only matters if you compile PyTorch from source, build custom CUDA extensions, or run software that depends on system-level CUDA libraries.
Two of those calls are easy to confuse. torch.backends.cuda.is_built() tells you whether PyTorch was built with CUDA support, and PyTorch’s documentation says plainly that this does not mean CUDA is available, only that the binary could use it on a machine with working drivers and devices. torch.cuda is lazily initialised, so importing it always succeeds, and is_available() is the call that tells you whether the system actually supports CUDA.
Why does torch.cuda.is_available() return False even when the versions look correct?
is_available() returns False when the binary, the driver or the device layer has broken the chain, and matching version strings only rule out some of those causes. A working nvidia-smi output confirms only hardware health. It does not show that your PyTorch build can use it. Go through the causes in this order:
| What you see | What it means | What to change |
|---|---|---|
torch.version.cuda is None, version string ends in +cpu |
A CPU-only wheel is installed | Reinstall from a CUDA wheel index |
torch.version.cuda is higher than the CUDA version in the nvidia-smi header |
The wheel was built for a newer CUDA version than the installed driver’s ceiling | Update the driver, or install an older wheel your driver supports |
is_built() is True, is_available() is False, versions fit |
The binary has CUDA support, but CUDA is not available on this machine | Check the driver and device setup, not the build |
Check the wheel suffix before touching the driver, and the driver before touching the toolkit.
How do I choose between cu121, cu128 and newer wheels for my driver and for sm_120?
Choose a CUDA variant that your driver supports and whose compiled architecture list includes your GPU. Both conditions have to hold. The suffix (cu121, cu128) names the CUDA version the wheel was built with, which is the same value torch.version.cuda will report. Which suffixes exist for a given PyTorch release follows PyTorch’s own release policy, which normally supports the latest two minor versions of accelerator software such as CUDA and ROCm, with exceptions handled case by case.
When Blackwell GPUs arrived, a PyTorch maintainer showed on the PyTorch Forums in February 2025 that a CUDA 12.8 (cu128) nightly build listed sm_120 and compute_120 in torch.cuda.get_arch_list() and ran a tensor on a Blackwell GPU. Those first nightlies were Linux x86 only, for Python 3.9 to 3.13, and Windows nightlies followed later that month.
What the architecture number actually limits, beyond which wheel you pick, is the subject of what compute capability constrains for AI workloads.
What torch.version.cuda and torch.version.hip show on ROCm, Intel xpu and CPU builds
On a ROCm build, PyTorch deliberately reuses the torch.cuda interfaces, so existing code needs very few changes, if any. AMD’s ROCm documentation describes torch.cuda as the module for managing both AMD and NVIDIA GPUs, with ROCm or CUDA underneath, and upstream PyTorch generates HIP source from its CUDA backend automatically. Your device string stays "cuda". torch.version.hip carries the HIP version, and torch.version.cuda is None.
PyTorch’s HIP notes warn against treating the two version systems as interchangeable: CUDA_VERSION, cudaRuntimeGetVersion and cudaDriverGetVersion do not map to the same values as their HIP counterparts. Any version check that compares a HIP number against a CUDA threshold is wrong by construction. ROCm also has its own release rhythm. AMD notes that the ROCm PyTorch release may lag the latest stable PyTorch, and the official PyTorch release may lag the latest ROCm. Some backend choices exist only on ROCm, such as choosing between AOTriton and composable_kernel for Flash Attention.
Intel GPUs use a separate device name. torch.xpu is the API for Intel GPU support, and moving code across means changing "cuda" references to "xpu". Intel GPU support arrived as a prototype in PyTorch 2.5 for Intel client GPUs and the Data Center GPU Max Series, on Linux and Windows. On these builds, the visibility check is torch.xpu.is_available(), not the torch.cuda call.
On a CPU-only build, torch.version.cuda is None and the version string ends in +cpu. torch.version.hip is None as well.
Moving code between stacks is an engineering question, which TechnoLynx’s AI Porting & Deployment Pack takes on one workload at a time.
The version string belongs to the benchmark result
Everything above amounts to one point. The CUDA number PyTorch reports describes software, and performance belongs to the hardware and that software together. LynxBenchAI treats the AI Executor (a device plus the software stack that drives it) as the unit of measurement. Every result is bound to the device, the backend it ran through, and the driver, framework and runtime on that machine, and no score is attributed to the silicon alone.
The 26Q3 results show why this matters. The GeForce RTX 5090 appears twice: on native Linux it scores 515.3 in training, 985.8 in inference and 279.6 in compute, and under WSL it scores 470.3, 891.8 and 261.4. The GPU model is the same, but the software environment is different, so these are two executors with two sets of numbers. The direction is not fixed either. In the same release, the RTX 3080 under WSL scores 72.5 in compute against 64.8 on native Linux, while it scores lower under WSL in training and inference. We would not read a cause into these differences.
The same reasoning covers vendors. LynxBenchAI runs one catalogue under one set of rules on NVIDIA through cuda, on AMD through cuda via ROCm, on Intel through xpu, and on CPU. In 26Q3, that is how the AMD Instinct MI300X and the Intel Graphics in the Core Ultra 7 165H come to sit in the same table as NVIDIA parts. Optimisation effort is capped in the same way for all of them: models are prepared once, before any target device is known, so a deeper ecosystem shows up in how the workload executes, not in extra hand-tuning.
To see what your own executor does, the Personal Edition installs with pip install lynxbench-ai. It runs the release catalogue, not your production model, and its runs submit to the public leaderboard. You can then compare a claim about your GPU and driver combination with runs other people produced under the same release, and you can see which combinations nobody has run yet.
Before reading any two PyTorch benchmark numbers side by side, check whether they came from the same torch.version.cuda, the same driver and the same backend. If they did not, you are comparing two executors, even when the GPU model is the same.