1.1 万 Star 的 CuPy,把 NumPy 一行不改搬到 GPU 上
- CuPy 提供与 NumPy/SciPy 几乎一致的 API,仅需修改 import 即可将代码迁移到 GPU 运行
- drop-in replacement 持续逼近但未 100% 完成,冷门函数和边界行为仍有缺口
- 与 PyTorch 定位不同,CuPy 专注数值计算无自动求导,且支持 AMD ROCm
- 支持直接编写 CUDA kernel 和调用底层 API,实现高层易用与底层性能兼顾
- 由 Preferred Networks 长期维护,十年 153 个 release,合并 cuSignal 扩展信号处理能力
先说一个很多做 AI 的人会忽略的事实。
今天大家一提到 GPU 加速,第一反应就是 PyTorch、TensorFlow,好像 GPU 只能用来训练模型。但科学计算、信号处理、图像处理这一大堆「不碰深度学习但也要算矩阵」的活,其实更需要 GPU。
这些领域的代码底座是 NumPy 和 SciPy。问题来了,你要把这些 NumPy 代码搬到 GPU 上,难道要把几十万行 np.dot、scipy.fft 全部重写成 PyTorch 的 torch.matmul?
太亏了。
cupy/cupy 就是来解决这个问题的。截止 2026 年 6 月 28 日,1.13 万 Star,1.06 万 Fork,MIT 协议。它干的事一句话能说清,做一套和 NumPy/SciPy API 几乎完全一样的接口,但底层跑在 GPU 上。你原来的代码改个 import,import numpy as np 换成 import cupy as cp,就能吃 GPU 的算力。
最让我敬佩的是它的寿命。这个项目 2016 年 11 月创建,到现在快十年了,已经发了 153 个 release,最新一个 v14.1.1 是 6 月 1 日。十年坚持维护一个 NumPy 的 GPU 替身,这在浮躁的开源圈是相当难得的。
drop-in replacement,这四个字值千金
CuPy 最核心的卖点,README 里反复强调一个词,drop-in replacement,即插即用的替代品。
什么意思呢,看这段代码。
import cupy as cp
x = cp.arange(6).reshape(2, 3).astype('f')
x.sum(axis=1)
# array([[ 3., 12.]], dtype=float32)
换成 NumPy 就是把 cp 改成 np,其余一字不改。arange、reshape、astype、sum,全在。这种 API 级别的对齐,是 CuPy 团队十年磨出来的功夫。
为什么这件事这么重要。因为真实的科学计算代码库,动辄几万行 NumPy 调用。如果你要迁到 PyTorch,得逐行改 API,得调 dtype 行为差异,得处理设备迁移。工程量大到很多人干脆放弃 GPU。CuPy 把这个迁移成本压到接近零,这就是 drop-in 的价值。
它不只是模仿 NumPy。SciPy 的那一套也覆盖了,线性代数 scipy.linalg、稀疏矩阵 scipy.sparse、FFT scipy.fft,都有对应的 GPU 版本。topics 里那一串 cuda、cudnn、cublas、cusolver、cusparse、nccl、cutensor,全是 NVIDIA 的底层库,CuPy 把它们包成了 Python 能调的接口。
但 drop-in 从来没 100% 完成过
这是我在 GitHub issue 区挖到的、README 不会主动告诉你的事。
CuPy 有两个挂着很久的 tracker issue,一个 #6078「Implement all numpy.* APIs in CuPy」39 赞,一个 #6324「Implement all scipy.* APIs in CuPy」35 赞。这俩 issue 标题直白,就是「把所有 NumPy、SciPy 的 API 都实现一遍」。注意它们的状态,至今 open。
这意味着所谓的「drop-in replacement」是一个持续逼近、但从未 100% 达成的目标。高频 API 确实齐了,但冷门函数、边界行为,依然有缺口。迁移大项目前,真得跑一遍测试,别指望一行不改 100% 能跑。
更扎心的是性能并非处处领先。issue #5075「cp.matmul slower than torch.matmul」拿了 37 赞,报告者用同样规模的矩阵测,CuPy 的矩阵乘法比 PyTorch 慢。原因也好理解,PyTorch 对深度学习场景的算子做了极致优化,而 CuPy 走的是通用数值计算路线,某些热点算子反而吃亏。所以「换 import 就快几十倍」这种话,对元素级运算成立,对部分线性代数运算要打问号。
这两条是 README 里没有的、issue 区里反复出现的真实摩擦。选 CuPy 之前,得先确认你的热点算子在它的甜区里。
它和 PyTorch 到底什么关系
很多人会问,既然 PyTorch 也能做矩阵运算,为啥还要 CuPy。
这俩定位完全不同。
PyTorch 是为深度学习设计的,它的核心是自动求导、计算图、反向传播。你要算个前向,它顺带把反向的图都建好了。这套机制对训练模型很爽,但你只是想做个 FFT 或者解个线性方程组,这套机制就是纯负担。
CuPy 不搞这些。它就是个数值计算库,没有自动求导,没有计算图,就是老老实实把数组算术放到 GPU 上跑。它的设计哲学更接近 NumPy,给数组,给算子,你自己组合。
还有一个关键差异,CuPy 同时支持 AMD ROCm。README 里明确写了,pip install cupy-rocm-7-0 就能装。虽然标着 experimental,但至少给了非 NVIDIA 显卡一条路。PyTorch 对 ROCm 的支持虽然也有,但 CuPy 这种「NVIDIA 和 AMD 用同一套 API」的承诺,对很多跨硬件的科研团队是有吸引力的。
坦白讲,如果你在搞深度学习,PyTorch 仍然是首选。但如果你手里是一堆遗留的 NumPy 科学计算代码,想加速又不想重写,CuPy 是目前最干净的路。
也能直接写 CUDA,这是它的另一面
CuPy 不只是个高级封装。它给你留了直通 GPU 底层的口子。
翻它的源码 cupy/_core/ 目录,会看到大量 .pyx 文件,_routines_math.pyx、_routines_linalg.pyx、_reduction.pyx。.pyx 是 Cython 的源文件,这意味着 CuPy 的核心算子是先用 Cython 写的 C 扩展,再编译进 Python。这也解释了它为什么能同时保持「Python 易用」和「C 级性能」,胶水层是 Cython 而不是纯 Python 包装。
如果你觉得自带的算子不够用,想自己写 kernel,它有 RawKernel,让你直接写 CUDA C/C++ 代码,编译成 GPU kernel 来跑。你还能用 Stream 控制异步执行流,甚至直接调 CUDA Runtime API。
# 伪代码示意
my_kernel = cp.RawKernel(source, 'my_func')
my_kernel((blocks,), (threads,), (args))
这个设计很聪明。它没有把底层焊死,普通的用 cp.xxx 就够了,要榨干性能的,可以下探到 CUDA C 层。一份代码里,高层 API 和底层 kernel 可以混着用。
这也是为什么我把它定位成「NumPy 的超集」而不是「NumPy 的复刻」。它复刻了 API,但能力远不止于此。topics 里的 nvtx(NVTX 标记)、nvrtc(运行时编译)这些,都是给极致性能场景准备的。
这十年是怎么活下来的
我比较好奇的是,一个数值计算库,凭什么能维护十年还有 1.1 万 Star。
答案藏在它的背景里。CuPy 由日本公司 Preferred Networks 主导开发和维护,社区贡献者参与。这家公司做的是 AI 和深度学习基础设施,CuPy 是他们技术栈的底座之一。
企业背书意味着什么,意味着这个项目不会因为作者兴趣转移而停更。十年里,GPU 计算从 CUDA 一家独大,到 ROCm 崛起,到 Apple Silicon 杀进来,CuPy 都跟上了。153 个 release,平均一年 15 个版本,这个节奏很稳。我查了最近 5 个版本,v14.0.0 到 v14.1.1,从 2 月到 6 月稳步推进,没有任何断更迹象。
它的语言占比也很有意思,Python 72.5%,Cython 18.5%,剩下才是 C++ 和 CUDA。这个结构说明它大量用 Cython 做胶水,把 Python 接口和底层 C/CUDA 库粘起来。这也是它能同时保持「Python 易用」和「C 级性能」的关键工程取舍。
还有一点值得一提,CuPy v13.0.0 把原来独立的 cuSignal 项目合并了进来。cuSignal 是 GPU 版的信号处理库,对标 scipy.signal。这个合并让 CuPy 的 SciPy 覆盖面又厚了一层。
哪些场景别用它
聊到这我得诚实点,CuPy 不是万能的。
数据搬运的开销绕不开。 GPU 算得快,但数据得从 CPU 内存搬到 GPU 显存,算完再搬回来。如果你的计算量太小,搬运的时间比算的时间还长,上 GPU 反而更慢。CuPy 适合的是「数据搬一次,算很久」的场景,不是那种频繁小计算的场景。
Apple Silicon 原生无缘。 README 的安装表里,pip 包只列了 x86_64 的 Linux 和 Windows,ROCm 也只标了 experimental。Mac 用户基本进不来。这也是它 Star 数不如 PyTorch 的原因之一,受众面窄。
部分算子比 PyTorch 慢。 前面说的 #5075 就是例子。如果你的工作负载恰好落在 CuPy 没优化透的算子上,drop-in 之后反而可能变慢。迁移前最好先 benchmark 你的热点函数。
它不解决分布式。 单机多卡它能通过 NCCL 搞定,但多机分布式计算它不管。要大规模分布式,你得看 Dask 或者其他框架。
drop-in 是一种被低估的工程美德
我对 CuPy 的感情,是一种对「老牌工具」的敬意。
在所有人都在追逐大模型、追逐 Transformer 的今天,有这么一个项目,十年如一日地维护着 NumPy 的 GPU 影子。它不搞花哨的 demo,不发惊人的 benchmark 截图,就是稳稳地把每一个 API 对齐,把每一种 GPU 适配好。这种工程态度,今天反而稀缺。
但跳出 CuPy 本身,我觉得它验证了一个可迁移的工程判断,「接口对齐」比「能力更强」更能降低迁移成本。CuPy 没有自动求导,没有计算图,论绝对能力远不如 PyTorch,可它靠「和 NumPy API 一一对应」这一件事,就接住了一大票无法重写的遗留科学计算代码。
这个判断放到工具选型上很通用。当你面对一堆不能动的历史代码,与其追新框架,不如找一个接口对齐的替身。drop-in replacement 的力量不在性能,在于它让「不重写」成为可能。CuPy 留给我们的,就是这么一条体面的路,不用学新框架,不用重写代码,换个 import,就能让跑了十年的 NumPy 脚本,在 GPU 上跑起来。
评论互动