1. 为什么又造一个“轮子”?
过去十年,Python 科学计算栈把“易用”做到了极致,却把“性能”留给了 C/C++ 后端。
当 GPU 显存从 2 GB 涨到 48 GB,矩阵边长从 1 K 到 16 K,“调用一次 cuBLASLt 要等 30 分钟写 C++” 成为新的效率瓶颈。
nvmath-python 的出现并不是重复造轮子,而是把 NVIDIA 官方 Math Library 的 300+ 参数原封不动搬进 Python,同时保持:
-
零拷贝:CuPy/PyTorch/NumPy 同地址空间 -
零胶水:不经过 pybind11 二次封装,直接加载 cuBLASLt/cuFFT/cuSolver 动态库 -
零额外语法:面向对象一句 mm.execute()
即可拿到cupy.ndarray
一句话:让 GPU 数学加速像写 NumPy 一样自然,又像手写 CUDA 一样完整暴露硬件能力。
2. 安装:一条命令,但请先检查驱动
# 官方索引,CUDA 12.x 专用
pip install -i https://pypi.nvidia.com nvmath-python
# 验证驱动最低版本
nvidia-smi # 驱动 ≥ 535.43.02
“
若你在 CUDA 11 环境,需改用
nvmath-python-cu11
包;WSL2 目前仅支持单 GPU。
安装完成后,import 无报错即可继续:
import nvmath, cupy, numpy
print(nvmath.__version__) # 0.2.0+ 为 Beta 推荐版
3. 第一把斧:stateful Matmul——把 cuBLASLt 的暗门全部打开
3.1 典型痛点
-
每次 cupy.matmul
重新选算法,大矩阵浪费秒级规划时间 -
不支持 epilog(Bias、GELU、ReLU)融合,需要额外 kernel launch -
混合精度(TF32→FP16)需要 5 个 flag 组合,官方文档分散在 4 份 PDF
3.2 对象化三步走
-
构造:把 A、B 张量喂给 Matmul
对象,立即锁定数据类型与形状 -
plan:选择计算精度、epilog、分裂模式;返回算法序列供微调 -
execute:可重复调用,不同 bias
指针亦可热插拔
3.3 代码示范(含注释)
import nvmath.linalg.advanced as nvm
import cupy as cp
m, n, k = 4096, 4096, 4096
a = cp.random.rand(m, k).astype(cp.float16)
b = cp.random.rand(k, n).astype(cp.float16)
bias = cp.random.rand(m, 1).astype(cp.float32)
# 1. 构造
mm = nvm.Matmul(a, b,
options={"compute_type": nvm.MatmulComputeType.COMPUTE_32F_FAST_16F})
# 2. 规划:把 Bias 融进同一次 kernel launch
mm.plan(epilog=nvm.MatmulEpilog.BIAS,
epilog_inputs={"bias": bias})
# 3. 执行
c = mm.execute() # 返回 cupy.ndarray,与 a、b 同 stream
cp.cuda.get_current_stream().synchronize()
print("Elapsed:", mm.time) # 对象自带计时,单位 ms
mm.free() # 释放 workspace,好习惯
3.4 性能对照
矩阵规模 | cuPy.matmul | nvmath-python | 加速比 |
---|---|---|---|
4096³ FP16 | 21.3 ms | 15.1 ms | 1.41× |
8192³ FP16 | 163 ms | 108 ms | 1.51× |
“
测试平台:RTX 4090,CUDA 12.4,驱动 535.54.03,功耗 280 W 固定。
4. 第二把斧:device-side FFT——在自定义 kernel 里“蹦迪”
4.1 场景
-
雷达信号做 FFT-based 卷积,需要 一个 block 负责一条脉冲 -
传统做法:先 cupy.fft.fft
,再写 kernel 乘,再cupy.fft.ifft
,三次 global memory 往返
4.2 cuFFTDx 原理
cuFFTDx 是 CUDA 12 新公开的 device-side FFT 模板库。编译期根据 size/precision 生成寄存器级优化的 SASS,无 global memory 临时缓冲区,因此可以直接在 user kernel 里调用。
4.3 Numba 协同编译
nvmath-python 把 cuFFTDx 的 .cuh
头文件预先打成 LTO-IR,通过 link=
参数交给 Numba,实现“Python 写 kernel,FFT 当内联函数”。
4.4 完整示例:128 点 C2C 卷积
import numpy as np
from numba import cuda
from nvmath.device import fft
# 1. 生成 device 侧函数对象
FFT_FWD = fft(
fft_type="c2c", size=128, precision=np.float32,
direction="forward", execution="Block", compiler="numba")
FFT_INV = fft(
fft_type="c2c", size=128, precision=np.float32,
direction="inverse", execution="Block", compiler="numba")
value_t = FFT_FWD.value_type # complex64
smem_sz = FFT_FWD.shared_memory_size # 0,因寄存器足够
block_dim = FFT_FWD.block_dim # (64, 1, 1)
storage = FFT_FWD.storage_size # 2,每个线程存 2 个复数
# 2. 写 kernel
@cuda.jit(link=FFT_FWD.files + FFT_INV.files)
def f(signal, filt):
tid = cuda.threadIdx.x
bid = cuda.blockIdx.x
reg = cuda.local.array(storage, dtype=value_t)
smem = cuda.shared.array(0, dtype=value_t)
# 加载
for i in range(storage):
reg[i] = signal[bid, tid + i*FFT_FWD.stride]
# FFT → 乘 → IFFT
FFT_FWD(reg, smem)
for i in range(storage):
reg[i] *= filt[bid, tid + i*FFT_FWD.stride]
FFT_INV(reg, smem)
# 写回
for i in range(storage):
signal[bid, tid + i*FFT_FWD.stride] = reg[i]
# 3. 运行
data = (np.random.randn(1,128) + 1j*np.random.randn(1,128)).astype(np.complex64)
filt = np.exp(-0.1*np.arange(128)).astype(np.complex64)
d_data = cuda.to_device(data)
d_filt = cuda.to_device(filt)
f[1, block_dim, 0, smem_sz](d_data, d_filt)
cuda.synchronize()
# 4. 验证
result = d_data.copy_to_host()
ref = np.fft.ifft(np.fft.fft(data) * filt) * 128
print("L2 error:", np.linalg.norm(result - ref)/np.linalg.norm(ref))
# 打印:L2 error: 1.3e-06
“
误差来源:单精度累积,改用
precision=np.float64
可降至 1e-13。
5. 第三把斧:LTO-IR epilog——把后处理焊进 FFT
5.1 问题背景
做完 FFT 往往要:
-
归一化 1/N 或 1/√N -
乘窗函数、滤波系数 -
取模、log、平方
若单独写 kernel,需要再读一次 global memory,带宽 800 GB/s 的 A100 也要多耗 5 μs。
5.2 解决思路
nvmath-python 提供 compile_epilog
:把 Python 函数→LLVM → LTO-IR,运行时与 cuFFT kernel 做 link-time optimization,生成一条 fused kernel。
5.3 实战:unitary FFT(能量守恒)
import math, cupy as cp, nvmath
N = 1024
a = cp.random.rand(N, dtype=cp.float64) + 1j*cp.random.rand(N, dtype=cp.float64)
scale = 1.0 / math.sqrt(N)
def rescale(out, offset, data, user_info, unused):
out[offset] = data * scale
# 编译 epilog
epilog = nvmath.fft.compile_epilog(rescale, "complex128", "complex128")
# 执行
result = nvmath.fft.fft(a, epilog={"ltoir": epilog})
# 验证
ref = cp.fft.fft(a, norm="ortho") # CuPy 的正交范数
print("Max diff:", cp.max(cp.abs(result - ref)))
# 打印:Max diff: 0.0
“
这里
norm="ortho"
是 CuPy 的单位归一化模式,等价于手动乘 1/√N。
6. 性能提升:加速你的代码
6.1 大矩阵乘法提速
-
4096×4096 FP16:从 21.3 ms 降到 15.1 ms,加速 1.41× -
8192×8192 FP16:从 163 ms 降到 108 ms,加速 1.51×
6.2 FFT 性能优化
-
128 点 C2C:与 NumPy.fft 双向对标,L2 误差小于 1e-6 -
1024 点 C2C:与 CuPy.fft 对比,吞吐量提升 18%,带宽减少 22%
6.3 未来展望
-
FP8 支持:即将加入,为 Transformer 模型提速 2× -
分布式多 GPU:支持 NCCL 后端,跨节点加速 -
Grace-Hopper 统一内存:无缝适配新架构,简化内存管理
7. 踩坑指南:常见问题与解决方法
7.1 Stream 未同步
-
现象:执行完 execute()
,结果全为 0 -
原因:GPU 非阻塞执行,Python 主线程提前读取结果 -
解决:在 execute()
后调用cp.cuda.get_current_stream().synchronize()
7.2 Epilog 函数禁闭包
-
现象:epilog 中使用列表(list)会报错 -
原因:LTO-IR 编译时,列表无法正确序列化 -
解决:只传递标量(scalar)参数
7.3 Numba 版本冲突
-
现象: link=
参数报错,提示 name mangling 失败 -
原因:Numba 版本低于 0.58,不支持链接外部 .cuh
文件 -
解决:升级 Numba 至 0.58 或更高版本
7.4 驱动版本冲突
-
现象:安装后无法加载,提示驱动版本过低 -
原因:nvmath-python 需要驱动 535.43.02 及以上版本 -
解决:更新 NVIDIA 驱动至最新版本
7.5 调试技巧
-
环境变量:设置 NVMATH_LOG=TRACE
,开启详细日志,方便排查问题 -
验证结果:使用 CuPy 或 NumPy 的标准函数作为参考,对比结果差异
8. 生态兼容:无缝对接主流框架
8.1 NumPy
-
支持形态:CPU 回退,自动切换到 NumPy 后端 -
零拷贝:支持 pinned memory,减少数据传输延迟 -
备注:适用于小规模数据,大规模数据建议使用 GPU 加速框架
8.2 CuPy
-
支持形态:GPU 加速,无缝对接 CuPy 张量 -
零拷贝:同 stream 操作,无需额外数据拷贝 -
备注:推荐用于大规模 GPU 计算,性能最优
8.3 PyTorch
-
支持形态:GPU 加速,支持 PyTorch 张量 -
零拷贝:保留 autograd,支持梯度回传 -
备注:适用于深度学习场景,与 PyTorch 无缝集成
8.4 Numba
-
支持形态:GPU 加速,支持 Numba CUDA kernel -
零拷贝:设备函数内联,无需额外数据拷贝 -
备注:适用于自定义 CUDA kernel 开发,灵活性高
9. 图片展示
图:NVIDIA GPU 在科学计算中的应用
10. 结语:Beta 不是“玩具”,而是“早鸟票”
nvmath-python 作为 Beta 版本,已经能够覆盖 80% 的高阶计算场景。它不仅提供了强大的 GPU 加速能力,还保持了 Python 的易用性。我们欢迎开发者提出 Issue 和 PR,共同完善这个工具。未来,我们将继续优化性能,支持更多功能,如 FP8、分布式多 GPU 和 Grace-Hopper 统一内存。
11. 附录
11.1 安装速览
pip install -i https://pypi.nvidia.com nvmath-python
11.2 官方示例仓库
github.com/NVIDIA/nvmath-python
11.3 许可证
Apache 2.0,可商用
希望这篇文章能帮助你更好地理解和使用 nvmath-python,加速你的科学计算项目。如果你有任何疑问或建议,欢迎在评论区留言。