Written and maintained by CASRAI Editorial Board
Last updated
CuPy is a Python array library that runs NumPy and SciPy style code on a GPU. This guide is for researchers who already use NumPy and want to know what CuPy is, how to install it, how to move an array to the GPU and back, where it behaves differently from NumPy, and how to time it correctly. All commands and behaviours below are taken from the official CuPy documentation (read 2026-11-07).
What is CuPy and what is it used for in research?
The CuPy documentation describes it as a NumPy/SciPy-compatible array library for GPU-accelerated computing with Python. Its cupy.ndarray supports the same dtypes as NumPy and follows NumPy semantics for indexing and broadcasting, so array code often needs only the import changed. Beyond NumPy routines (module-level functions, linear algebra, FFT, random numbers), cupyx.scipy covers SciPy areas such as FFTs, advanced and sparse linear algebra, image processing, special functions, signal processing and statistics, and cupyx.scipy.sparse provides sparse arrays and matrices.
In research, that means numerical work built on large dense arrays (simulation grids, image stacks, spectra, matrix factorisations) can be moved to a GPU without rewriting it in CUDA C. Routines are backed by NVIDIA CUDA libraries including cuBLAS, cuFFT, cuSPARSE, cuSOLVER and cuRAND, together with Thrust, CUB and cuTENSOR. For the underlying linear-algebra interface, see the CASRAI guide to BLAS (Basic Linear Algebra Subprograms).
What is the difference between NumPy and CuPy?
NumPy runs on the CPU and keeps arrays in host memory. CuPy keeps arrays in GPU memory and runs operations as GPU kernels. The API is intentionally similar, but the CuPy documentation lists differences that can change a result if you port code unchanged:
- Casting negative values to unsigned integers. Casting -1 to
uint32gives 4294967295 in NumPy and 0 in CuPy. - Out-of-bounds indices wrap. NumPy raises
IndexError; CuPy wraps the index around. - Duplicate indices in assignment. With
__setitem__, which value is stored for a repeated index is undefined in CuPy. - Reductions return 0-dimensional arrays rather than Python or NumPy scalars, which avoids a GPU-to-CPU synchronisation.
- No string or object dtypes.
- Mixing array types is an error. Universal functions reject lists and NumPy arrays (
TypeError), and a single operation cannot combine NumPy and CuPy arrays. - Memory layout. Strides and layout are not guaranteed to match NumPy.
- Random-number methods accept a
dtypeargument.
Can I use NumPy with CUDA?
Not directly: NumPy itself runs on the CPU. CuPy is the NumPy-compatible library that executes on NVIDIA CUDA GPUs; the overview page also states that it translates CUDA API calls to ROCm HIP so the same code can run on AMD GPUs. You can keep NumPy for CPU-side work and move arrays across with cupy.asarray and cupy.asnumpy (below).
How does CuPy work?
When you call a CuPy function, it launches work on the current GPU device (GPU 0 by default) using the CUDA libraries named above or a compiled kernel. Two behaviours matter for research timing: GPU execution is asynchronous, and the first call compiles kernels (cached in the process and on disk, with the cache directory controlled by CUPY_CACHE_DIR) and creates the CUDA context, which can take seconds.
Install or access (official source only)
Requirements from the installation page (read 2026-11-07): an NVIDIA CUDA GPU with compute capability 3.0 or higher; CUDA Toolkit 12.0 to 13.2 (optional if CUDA components come from PyPI or conda-forge, but the driver is still needed); Python 3.10 to 3.14; NumPy 2.0 to 2.4; and optionally SciPy 1.14 to 1.17.
With pip
pip install cupy-cuda12x
pip install cupy-cuda13x
pip install "cupy-cuda12x[ctk]"
Choose the package that matches your CUDA major version. The [ctk] form installs the CUDA components through pip so only the driver is required. Install only one CuPy package in an environment; check with pip freeze | grep cupy.
With conda
conda install -c conda-forge cupy
The conda-forge package includes CUDA runtime libraries; cupy-core is a minimal variant, and you can pin the CUDA build with cuda-version=12.9.
Step-by-step: first array, round trip and a fair benchmark
- Create an array on the GPU.
import cupy as cp, thenx_gpu = cp.array([1, 2, 3]). Check where it lives withx_gpu.device. - Compute on it. For example
cp.linalg.norm(x_gpu). - Move host data to the GPU.
x_gpu = cp.asarray(x_cpu)for a NumPy arrayx_cpu; no copy is made if the data is already on the device. - Bring a result back.
cp.asnumpy(x_gpu)orx_gpu.get()returns a NumPy array. - Select another GPU.
with cp.cuda.Device(1):runs the block on device 1. - Write code for either library.
xp = cp.get_array_module(x)returns the right module (NumPy or CuPy) for an array. - Benchmark correctly. Because GPU calls are asynchronous,
time.perf_countermeasures only the time to issue the work. Use the built-in helper, which also warms up first:from cupyx.profiler import benchmark print(benchmark(my_func, (a,), n_repeat=20))It reports both CPU and GPU times.
Output and how to read it
Print a CuPy array and you see its values like a NumPy array, but type(x) is cupy.ndarray. A reduction such as a sum or norm returns a 0-dimensional CuPy array, so convert it deliberately (for example float(result) or .get()) when you need a Python number; doing so synchronises with the GPU. In benchmark output, compare the reported GPU time for the GPU path against CPU time for a NumPy version of the same function, using the same data and excluding the first, kernel-compiling call. Speed-ups depend on array size and operation, so report your own measured numbers with the hardware, CUDA version and CuPy version, as the reproducibility of computational results depends on them.
Troubleshooting
| Symptom | What the CuPy installation page advises |
|---|---|
| Install fails to build or resolve | Upgrade pip and setuptools, and re-run with -vvvv for verbose output. |
NVRTC_ERROR_COMPILATION about missing vector_types.h on CUDA 12.2 or later |
Install the matching runtime headers, for example pip install "nvidia-cuda-runtime-cu12==12.X.*", and inspect the setup with cupy.show_config(). |
CompileException at run time |
Set CUDA_PATH and LD_LIBRARY_PATH. CuPy looks for CUDA in this order: CUDA_PATH, the directory of nvcc, then /usr/local/cuda. |
| Strange import or version conflicts | Only one CuPy package may be installed; check pip freeze | grep cupy. |
TypeError when combining arrays |
Convert explicitly with cp.asarray or cp.asnumpy; NumPy and CuPy arrays cannot be mixed in one operation. |
Alternatives and when you may not need CuPy
| Option | Runs on | Relationship to CuPy (per the CuPy docs) |
|---|---|---|
| NumPy | CPU | The reference API that CuPy mirrors; the right baseline for any speed comparison, and needed for any CPU-side work and for the NumPy 2.0 to 2.4 versions CuPy requires. |
| CuPy on NVIDIA CUDA | NVIDIA GPU | The main supported platform. |
| CuPy on AMD ROCm | AMD GPU | CuPy translates CUDA calls to ROCm HIP so the same code can run. |
If your arrays are small or your workflow is dominated by Python loops, a GPU may not help. For related Python scientific tooling, see the CASRAI guides to NetworkX and XGBoost.
FAQ
What is a CuPy?
CuPy is a NumPy/SciPy-compatible Python array library for GPU computing, with cupy.ndarray as its main array type.
Can I use NumPy with CUDA?
NumPy runs on the CPU; use CuPy for the NumPy-style API on NVIDIA CUDA GPUs, and convert between them with cp.asarray and cp.asnumpy.
What is the difference between NumPy and CuPy?
Where the arrays live (host versus GPU memory) and a short list of semantic differences such as index wrap-around, 0-dimensional reduction results and no string or object dtypes.
How does CuPy work?
It dispatches array operations to CUDA libraries and compiled GPU kernels on the current device, asynchronously, caching compiled kernels after the first call.







