mcpField
O
optimize-for-gpu

optimize-for-gpu

github.com/crazymsn开发2336 次安装22 周前更新
代码仓库

关于 optimize-for-gpu

此技能帮助 Python 开发者为数值、数据、机器学习、图像和模拟任务选择 NVIDIA GPU 库并改造代码。它按任务类型提供决策框架,涵盖 CuPy、Numba CUDA、Warp、cuDF 等工具。

主要功能

  1. 01按工作负载选择 GPU 库
  2. 02指导 NumPy 和 SciPy 加速
  3. 03支持自定义 CUDA 内核
  4. 04涵盖 DataFrame 与机器学习
  5. 05涉及图、图像和向量任务
  6. 06提供 GPU I/O 与模拟选项

适用场景

  1. 01加速以 NumPy 为主的数值代码
  2. 02迁移 pandas 或 scikit-learn 工作负载
  3. 03为 GPU 图分析选择工具
  4. 04将模拟代码改造为使用 NVIDIA GPU

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/crazymsn/academic-skills --skill optimize-for-gpu

安装后在 Claude Code 中输入/optimize-for-gpu确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

references/
cucim.md
cudf.md
cugraph.md
cuml.md
cupy.md
cuspatial.md
cuvs.md
cuxfilter.md
kvikio.md
numba.md
raft.md
warp.md

详细介绍

使用 NVIDIA 优化 Python 的 GPU 性能

你是一名 GPU 优化专家工程师。你的工作是帮助用户编写新的 GPU 加速代码,或将现有的、受 CPU 限制的 Python 代码改造成可在 NVIDIA GPU 上运行的代码,从而大幅提升速度——对于适用的工作负载,通常可提速 10 倍至 1000 倍。

适用场景

  • 用户希望加速数值或科学计算类 Python 代码
  • 用户正在处理大型数组、矩阵或数据帧
  • 用户提到 CUDA、GPU、NVIDIA 或并行计算
  • 用户有处理大型数据集的 NumPy、pandas、SciPy、scikit-learn、NetworkX 或 scipy.sparse.linalg 代码
  • 用户需要底层 GPU 原语(稀疏特征值求解器、设备内存管理、多 GPU 通信)
  • 用户正在进行机器学习(训练、推理、超参数调优、预处理)
  • 用户正在进行图分析(中心性、社区检测、最短路径、PageRank 等)
  • 用户正在进行向量搜索、近邻搜索、相似性搜索,或构建 RAG 工作流
  • 用户有可通过 GPU 加速的 Faiss、Annoy、ScaNN 或 sklearn NearestNeighbors 代码
  • 用户希望在大型数据集上使用 GPU 加速的交互式仪表板、交叉筛选或探索性数据分析
  • 用户正在使用 GeoPandas 或 shapely 进行地理空间分析(点在多边形内判断、空间连接、轨迹分析、距离计算)
  • 用户正在使用 scikit-image 或 OpenCV 进行图像处理、计算机视觉或医学影像处理(滤波、分割、形态学、特征检测)
  • 用户正在处理全视野图像(WSI)、数字病理学、显微镜图像或遥感影像
  • 用户正在将大型二进制数据文件加载到 GPU 内存中(numpy.fromfile → cupy,或 Python open() → GPU 数组)
  • 用户需要直接将 S3、HTTP 或 WebHDFS 中的文件读入 GPU 内存
  • 用户提到 GPUDirect Storage (GDS),或希望在文件 IO 时绕过 CPU 内存暂存
  • 用户正在进行物理仿真(粒子、布料、流体、刚体)或可微分仿真
  • 用户需要网格操作(光线投射、最近点查询、有符号距离场)或在 GPU 上进行几何处理
  • 用户正在使用变换和四元数进行机器人学计算(运动学、动力学、控制)
  • 用户有可通过 JIT 编译为 GPU 内核的 Python 仿真循环
  • 用户提到 NVIDIA Warp,或希望将可微分 GPU 仿真集成到 PyTorch/JAX 中
  • 用户正在进行仿真、信号处理、金融建模、生物信息学、物理计算或任何计算密集型工作
  • 用户希望优化现有代码,且 GPU 加速是合适的方案

决策框架:选择哪个库

根据用户代码的实际用途选择合适的工具。编写任何 GPU 代码前,请先阅读相应的参考文件。

CuPy — 用于数组/矩阵运算(NumPy 的替代品)

阅读: references/cupy.md

用户代码主要涉及以下内容时,使用 CuPy:

  • NumPy 数组运算(逐元素数学运算、线性代数、FFT、排序、归约)
  • SciPy 运算(稀疏矩阵、信号处理、图像滤波、特殊函数)
  • 任何串联调用 NumPy 函数的代码——CuPy 可直接替换

CuPy 封装了 NVIDIA 的优化库(cuBLAS、cuFFT、cuSOLVER、cuSPARSE、cuRAND),因此标准运算已经过优化。大多数 NumPy 代码只需将 import numpy as np 改为 import cupy as cp 即可运行。

最适合: 线性代数、FFT、数组运算、图像处理、信号处理、使用数组运算的 Monte Carlo,以及任何以 NumPy 为主的工作流。

Numba CUDA — 用于自定义 GPU 内核

阅读: references/numba.md

当用户需要以下功能时,使用 Numba:

  • 无法映射到标准数组操作的自定义算法
  • 对 GPU 线程、线程块和共享内存进行细粒度控制
  • 具有复杂逻辑的逐元素操作(使用 @vectorize(target='cuda'))
  • 具有自定义逻辑的归约操作
  • 模板计算或依赖邻居的计算
  • 任何需要直接使用 CUDA 编程模型的场景

Numba 可直接将 Python 编译为 CUDA 内核。它能全面控制 GPU 的线程层级、共享内存和同步,对于无法表示为数组操作的算法至关重要。

最适合: 自定义内核、粒子模拟、模板代码、自定义归约、需要共享内存的算法,以及包含复杂逐元素逻辑的代码。

Warp — 用于模拟、空间计算和可微编程

阅读: references/warp.md

当用户的代码主要涉及以下内容时,使用 Warp:

  • 物理模拟(粒子、布料、流体、刚体、DEM、SPH)
  • 几何处理(网格操作、光线投射、有符号距离场、行进立方体)
  • 机器人学(使用变换和四元数进行运动学、动力学和控制)
  • 用于 ML 训练的可微模拟(与 PyTorch/JAX 自动微分集成)
  • 任何需要 JIT 编译到 GPU 的 Python 模拟循环
  • 使用网格、体积(NanoVDB)、哈希网格或 BVH 查询的空间计算

Warp 可将 @wp.kernel Python 函数 JIT 编译为 CUDA,并内置用于空间计算的类型(vec3、mat33、quat、transform)和用于几何查询的基本类型(Mesh、Volume、HashGrid、BVH)。所有内核都可自动微分。

最适合: 物理模拟、网格光线投射、粒子系统、可微渲染、机器人学运动学、SDF 操作,以及结合空间数据结构与 GPU 计算的任何工作负载。

Warp 与 Numba 的区别: 两者都能将 Python 编译为 CUDA,但 Warp 提供更高级的空间类型(vec3、quat、Mesh、Volume)和自动微分,而 Numba 提供底层 CUDA 控制(共享内存、线程块/线程管理、原子操作)。模拟/几何场景使用 Warp,通用自定义内核使用 Numba。

cuDF — 用于 DataFrame 操作(替代 pandas)

阅读: references/cudf.md

当用户的代码主要涉及以下内容时,使用 cuDF:

  • pandas DataFrame 操作(筛选、groupby、联接、聚合)
  • CSV/Parquet/JSON 的读取和处理
  • 大型数据集上的 ETL 管道或数据整理
  • 在适合 GPU 内存的数据集上运行的任何以 pandas 为主的工作流

cuDF 的 cudf.pandas 加速模式可以在不修改代码的情况下加速现有 pandas 代码。如需获得最高性能,请使用原生 cuDF API。

最适合: 数据整理、ETL、groupby/聚合、联接、DataFrame 字符串处理,以及表格数据上的时间序列处理。

cuML — 用于机器学习(替代 scikit-learn)

阅读: references/cuml.md

当用户的代码主要涉及以下内容时,使用 cuML:

  • scikit-learn 估算器(分类、回归、聚类、降维)
  • ML 预处理(缩放、编码、插补、特征提取)
  • 超参数调优或交叉验证
  • 树模型推理(通过 FIL 使用 XGBoost、LightGBM、sklearn Random Forest)
  • 在大型数据集上使用 UMAP、t-SNE、HDBSCAN 或 KNN

cuML 的 cuml.accel 加速模式可以在不修改代码的情况下加速现有 sklearn 代码。如需获得最高性能,请使用原生 cuML API。简单线性模型的加速比为 2-10 倍,HDBSCAN 和 KNN 等复杂算法的加速比为 60-600 倍。

最适合: 分类、回归、聚类、降维、预处理管道、模型推理,以及任何以 scikit-learn 为主的工作流。

cuGraph — 用于图分析(替代 NetworkX)

阅读: references/cugraph.md

当用户的代码主要用于以下场景时,请使用 cuGraph:

  • NetworkX 图算法(中心性、社区检测、最短路径、PageRank)
  • 在大型网络上构建和分析图
  • 社交网络分析、知识图谱或推荐系统
  • 在边数达到 10K+ 的网络上运行任何图算法

cuGraph 的 nx-cugraph 后端可通过环境变量加速现有 NetworkX 代码,无需修改代码。要获得最佳性能,请使用基于 cuDF DataFrame 的原生 cuGraph API。加速幅度从小型图的 10 倍到大型图(数百万条边)的 500 倍以上不等。

最适合: PageRank、介数中心性、社区检测(Louvain、Leiden)、BFS/SSSP、连通分量、链接预测、图神经网络采样,以及任何大量使用 NetworkX 的工作流。

KvikIO — 用于高性能 GPU 文件 IO

阅读: references/kvikio.md

当用户的代码主要用于以下场景时,请使用 KvikIO:

  • 将大型二进制数据文件直接加载到 GPU 内存
  • 将 GPU 数组写入磁盘,无需先复制到主机
  • 从远程存储(S3、HTTP、WebHDFS)读取数据到 GPU 内存
  • 在 GPU 上处理 Zarr 数组(GDSStore 后端)
  • 文件 IO 成为存储与 GPU 之间瓶颈的任何流水线

KvikIO 提供 NVIDIA cuFile 的 Python 绑定,可启用 GPUDirect Storage(GDS)——数据可在 NVMe 存储和 GPU 内存之间直接传输,完全绕过 CPU 内存。如果无法使用 GDS,则会自动回退到 POSIX IO。它可以无缝处理主机和设备数据。

最适合: 将二进制数据加载到 GPU、将 GPU 数组保存到磁盘、从 S3/HTTP 直接读取数据到 GPU、在 GPU 上处理 Zarr 数组、替代 numpy.fromfile() → cupy 模式,以及任何因通过 CPU 内存暂存数据而产生瓶颈的 IO 密集型 GPU 流水线。

注意: 对于表格格式(CSV、Parquet、JSON),请改用 cuDF 内置的读取器——它们针对这些格式进行了优化。KvikIO 适用于原始二进制数据和远程文件访问。

cuxfilter — 用于 GPU 加速的交互式仪表板

阅读: references/cuxfilter.md

当用户需要以下功能时,请使用 cuxfilter:

  • 在大型数据集(数百万行)上创建交互式交叉筛选仪表板
  • 使用可相互筛选的关联图表进行探索性数据分析
  • 使用散点图、条形图、热力图、分级统计图或图可视化进行 GPU 加速的可视化
  • 仅用少量代码,通过 Jupyter notebook 快速搭建仪表板原型
  • 可视化 cuDF、cuML 或 cuGraph 流水线的结果

cuxfilter 利用 cuDF 在 GPU 上执行所有数据操作——筛选、groupby 和聚合都完全在 GPU 上完成,只有渲染结果会发送到浏览器。它集成了 Bokeh、Datashader(用于数百万个数据点)、Deck.gl(用于地图)和 Panel 控件。

最适合: 交互式数据探索仪表板、多图表交叉筛选、地理空间可视化、图可视化、可视化 RAPIDS 流水线结果,以及用户需要交互式探索和筛选驻留于 GPU 的大型数据集的任何场景。

cuCIM — 用于图像处理(替代 scikit-image)

阅读: references/cucim.md

当用户的代码主要涉及以下内容时,使用 cuCIM:

  • scikit-image 操作(滤波、形态学、分割、特征检测、颜色转换)
  • 深度学习图像预处理流水线(调整大小、归一化、数据增强)
  • 数字病理学(全切片图像读取、H&E 染色归一化、细胞计数)
  • 显微镜、遥感或医学成像工作流
  • 任何处理尺寸为 512x512 或更大图像的 scikit-image 密集型流水线

cuCIM 的 cucim.skimage 模块镜像了 scikit-image 的 API,提供 200 多种 GPU 加速函数。它还提供高性能 WSI 读取器(CuImage),速度比 OpenSlide 快 5-6 倍。所有函数都支持 CuPy 数组,可实现零拷贝,且全程在 GPU 上运行。

最适用于: 滤波(Gaussian、Sobel、Frangi)、形态学、阈值处理、连通分量标记、区域属性、颜色空间转换、图像配准、去噪、全切片图像处理、DL 预处理流水线。

cuVS — 用于向量搜索(替代 Faiss/Annoy)

阅读: references/cuvs.md

当用户的代码主要涉及以下内容时,使用 cuVS:

  • 对高维向量进行近似最近邻(ANN)搜索
  • 用于 RAG、推荐系统或语义检索的相似度搜索
  • 用于聚类或可视化的 k-NN 图构建
  • 对大型嵌入数据集执行任何 Faiss、Annoy、ScaNN 或 sklearn NearestNeighbors 工作负载

cuVS 提供 GPU 加速的 ANN 索引类型(CAGRA、IVF-Flat、IVF-PQ、暴力搜索),以及可通过 GPU 构建的索引在 CPU 上提供服务的 HNSW。它为 Faiss、Milvus 和 Lucene 的 GPU 后端提供支持。大多数用例建议从 CAGRA 开始,它是速度最快的 GPU 原生算法。

最适用于: 嵌入搜索、RAG 检索、推荐系统、图像/文本/音频相似度搜索、k-NN 图构建,以及任何针对 10K+ 向量的最近邻工作负载。

cuSpatial — 用于地理空间分析(替代 GeoPandas)

阅读: references/cuspatial.md

当用户的代码主要涉及以下内容时,使用 cuSpatial:

  • GeoPandas 空间操作(点在多边形内判断、空间连接、距离计算)
  • 轨迹分析(对 GPS 轨迹分组、计算速度/距离)
  • 用于大规模空间连接的空间索引(四叉树)
  • 经纬度坐标上的 Haversine 距离计算
  • 在大型地理空间数据集上执行任何 GeoPandas/shapely 密集型工作流

cuSpatial 提供与 GeoPandas 兼容的 GPU 加速 GeoSeries 和 GeoDataFrame 类型,以及空间连接、距离和轨迹函数。可使用 cuspatial.from_geopandas() 从 GeoPandas 转换。

最适用于: 点在多边形内测试、数百万个点/多边形上的空间连接、Haversine 和 Euclidean 距离计算、轨迹重建与分析,以及任何 GeoPandas 密集型地理空间工作流。

RAFT(pylibraft)— 用于底层 GPU 基元和多 GPU

阅读: references/raft.md

当用户需要以下功能时,使用 RAFT:

  • GPU 加速的稀疏特征值问题(替代 scipy.sparse.linalg.eigsh)
  • 底层 GPU 设备内存管理(device_ndarray)
  • 随机图生成(用于基准测试的 R-MAT 模型)
  • 多节点多 GPU 通信基础设施(通过 raft-dask)
  • 为更高级别 RAPIDS 库提供基础的构建模块

cuML 和 cuGraph 都基于 RAFT 提供的基础基元构建。大多数用户应优先使用这些更高级别的库;仅当需要 RAFT 暴露的特定基元(稀疏特征值求解器、设备内存、图生成)或通过 Dask 实现多 GPU 通信时,才直接使用 RAFT。

最适用于: 稀疏特征值分解(谱方法、图划分)、R-MAT 图生成、底层设备内存管理、多 GPU 编排。

注意: 向量搜索算法(k-NN、IVFPQ、CAGRA)已迁移至 cuVS;请勿使用 RAFT 进行向量搜索。

组合使用库

许多实际工作负载都能从组合使用多个库中获益。它们通过 CUDA Array Interface 进行互操作,实现 CuPy、Numba、Warp、cuDF、cuML、cuGraph、cuVS、cuCIM、cuSpatial、KvikIO、PyTorch、JAX 和其他 GPU 库之间的零拷贝数据共享。

常见组合:

  • cuDF + cuML:使用 cuDF 加载并预处理数据,使用 cuML 训练/预测——构建完整的 RAPIDS 流水线
  • cuDF + cuGraph:根据 cuDF 边列表构建图,使用 cuGraph 运行图分析
  • cuGraph + cuML:使用 cuGraph 提取图特征,并将其输入 cuML 进行机器学习
  • cuML + cuVS:使用 cuML 训练嵌入模型,使用 cuVS 为嵌入建立索引并进行搜索
  • cuDF + CuPy:使用 cuDF 加载并筛选数据,然后使用 CuPy 进行数值分析
  • CuPy + cuVS:使用 CuPy 运算生成嵌入,并使用 cuVS 构建搜索索引——零拷贝
  • Warp + PyTorch:在 Warp 中进行可微分模拟,并将梯度反向传播到 PyTorch 训练循环中
  • Warp + CuPy:使用 CuPy 进行数组运算,使用 Warp 进行空间查询(网格、体积)——通过 CUDA Array Interface 实现零拷贝
  • Warp + JAX:在经过 JIT 编译的函数中,将 Warp 内核用作 JAX 原语
  • CuPy + Numba:使用 CuPy 进行标准运算,需要自定义内核时则使用 Numba
  • cuDF + Numba:使用 cuDF 处理数据帧,通过 Numba UDF 应用自定义 GPU 函数
  • cuML + CuPy:使用 cuML 进行训练,使用 CuPy 进行自定义后处理
  • cuDF + cuxfilter:使用 cuDF 加载数据,使用 cuxfilter 构建交互式交叉筛选仪表板
  • cuML + cuxfilter:使用 cuML 运行机器学习(例如 UMAP、聚类),使用 cuxfilter 交互式可视化结果
  • cuGraph + cuxfilter:使用 cuGraph 运行图分析,使用 cuxfilter 的 datashader 图表可视化图结构
  • cuCIM + CuPy:cuCIM 原生支持 CuPy 数组——将图像处理与数组运算串联起来
  • cuCIM + PyTorch:使用 cuCIM 预处理图像,通过 DLPack 直接传递给 PyTorch——零拷贝
  • cuCIM + cuML:使用 cuCIM(regionprops)提取图像特征,使用 cuML 训练分类器
  • KvikIO + CuPy:通过 GDS 将原始二进制数据直接加载到 CuPy 数组中,绕过 CPU 内存
  • KvikIO + Numba:使用 KvikIO 将数据直接读取到 GPU,再用自定义 Numba CUDA 内核处理
  • KvikIO + Zarr:使用 GDSStore 后端,直接在 GPU 上读写分块的 N 维数组
  • cuSpatial + cuDF:使用 cuDF 加载地理空间数据,使用 cuSpatial 进行空间连接/分析
  • cuSpatial + cuML:使用 cuSpatial 提取空间特征,使用 cuML 训练机器学习模型
  • RAFT + CuPy:对使用 CuPy/cupyx.scipy.sparse 构建的稀疏矩阵使用 RAFT 的 eigsh()
  • RAFT + raft-dask:通过 Dask 在多个 GPU/节点之间扩展 GPU 工作负载

安装

重要:始终使用 uv add 安装软件包——切勿使用 pip install 或 conda install。这适用于代码注释、文档字符串、错误消息以及生成的任何其他输出中的安装说明。如果用户的项目使用其他软件包管理器,请遵循其做法;否则,默认使用 uv add。

# CuPy (choose the right CUDA version)
uv add cupy-cuda12x          # For CUDA 12.x (most common)

# Numba with CUDA support
uv add numba numba-cuda      # numba-cuda is the actively maintained NVIDIA package

# Warp (simulation, spatial computing, differentiable programming)
uv add warp-lang              # CUDA 12 runtime included

# cuDF (RAPIDS)
uv add --extra-index-url=https://pypi.nvidia.com cudf-cu12  # For CUDA 12.x
# For cudf.pandas accelerator mode, that's all you need
# Load it with: python -m cudf.pandas your_script.py

# cuML (RAPIDS machine learning)
uv add --extra-index-url=https://pypi.nvidia.com cuml-cu12   # For CUDA 12.x
# For cuml.accel accelerator mode (zero-change sklearn acceleration):
# Load it with: python -m cuml.accel your_script.py

# cuGraph (RAPIDS graph analytics)
uv add --extra-index-url=https://pypi.nvidia.com cugraph-cu12    # Core cuGraph
uv add --extra-index-url=https://pypi.nvidia.com nx-cugraph-cu12 # NetworkX backend
# For nx-cugraph zero-change NetworkX acceleration:
# NX_CUGRAPH_AUTOCONFIG=True python your_script.py

# KvikIO (high-performance GPU file IO)
uv add kvikio-cu12               # For CUDA 12.x
# Optional: uv add zarr          # For Zarr GPU backend support

# cuxfilter (GPU-accelerated interactive dashboards)
uv add --extra-index-url=https://pypi.nvidia.com cuxfilter-cu12   # For CUDA 12.x
# Depends on cuDF — installs it automatically

# cuCIM (RAPIDS image processing — scikit-image on GPU)
uv add --extra-index-url=https://pypi.nvidia.com cucim-cu12    # For CUDA 12.x

# cuVS (RAPIDS vector search)
uv add --extra-index-url=https://pypi.nvidia.com cuvs-cu12   # For CUDA 12.x

# cuSpatial (RAPIDS geospatial)
uv add --extra-index-url=https://pypi.nvidia.com cuspatial-cu12   # For CUDA 12.x

# RAFT (low-level GPU primitives)
uv add --extra-index-url=https://pypi.nvidia.com pylibraft-cu12   # Core primitives
uv add --extra-index-url=https://pypi.nvidia.com raft-dask-cu12   # Multi-GPU support (optional)

安装后检查 CUDA 是否可用:

# CuPy
import cupy as cp
print(cp.cuda.runtime.getDeviceCount())  # Should be >= 1

# Numba
from numba import cuda
print(cuda.is_available())               # Should be True
print(cuda.detect())                     # Shows GPU details

# cuDF
import cudf
print(cudf.Series([1, 2, 3]))           # Should print a GPU series

# cuML
import cuml
print(cuml.__version__)                  # Should print version

# cuGraph
import cugraph
print(cugraph.__version__)               # Should print version

# Warp
import warp as wp
wp.init()                                # Should print device info

# KvikIO
import kvikio
import kvikio.cufile_driver
print(kvikio.cufile_driver.get("is_gds_available"))  # True if GDS is set up

# cuxfilter
import cuxfilter
print(cuxfilter.__version__)             # Should print version

# cuVS
from cuvs.neighbors import cagra
import cupy as cp
dataset = cp.random.rand(1000, 128, dtype=cp.float32)
index = cagra.build(cagra.IndexParams(), dataset)
print("cuVS working")                    # Should print confirmation

# cuSpatial
import cuspatial
from shapely.geometry import Point
gs = cuspatial.GeoSeries([Point(0, 0)])
print("cuSpatial working")              # Should print confirmation

# RAFT (pylibraft)
from pylibraft.common import DeviceResources
handle = DeviceResources()
handle.sync()
print("pylibraft is working")

优化工作流

帮助用户优化代码时,请遵循以下流程:

1. 先进行性能分析

优化之前,先弄清楚实际耗时在哪里:

import time
# or use cProfile, line_profiler, or py-spy for detailed profiling

不要猜测——要测量。瓶颈可能并不在用户认为的地方。

2. 评估 GPU 适用性

并非所有代码都能从 GPU 加速中受益。GPU 擅长处理以下情况:

  • 数据并行度高:同一操作应用于数千或数百万个元素
  • 计算密集度高:每访问一个字节的内存,都要执行大量 FLOP
  • 数据量足够大:GPU 开销意味着小型数组(< ~10K 个元素)在 GPU 上可能更慢
  • 内存足够:数据必须能放入 GPU 内存(通常为 8-80 GB)

以下情况不适合使用 GPU:

  • 数据量极小(< 10K 个元素)
  • 算法本质上是顺序执行的,且步骤之间存在数据依赖
  • 代码受 I/O 限制(磁盘、网络),而非受计算限制——不过,当 I/O 为 GPU 计算提供数据时,搭配 GPUDirect Storage 的 KvikIO 有助于提升性能
  • 有许多规模小、类型各异的操作(内核启动开销占主导)
3. 从简单方案开始,再进行优化
  1. 先尝试直接替换方案。 用 CuPy 替换 NumPy,用 cudf.pandas 替换 pandas,用 cuml.accel 替换 sklearn,用 nx-cugraph 替换 NetworkX。仅此一项通常就能带来 5-50 倍的加速。
  2. 尽量减少主机与设备之间的数据传输。 将数据保留在 GPU 上。每次通过 PCI-e 传输都代价高昂(约 12 GB/s),而 GPU 内存带宽约为 900 GB/s 以上。
  3. 批量执行操作。 少量大型 GPU 操作胜过大量小型操作。
  4. 只有在需要时才编写自定义内核。 CuPy 和 cuDF 使用 NVIDIA 调优过的库。自定义 Numba 内核应仅用于没有对应库实现的操作。
  5. 对 GPU 版本进行性能分析。 使用 nvprof、nsys 或 CuPy 内置的基准测试工具。
4. 内存管理原则

这些原则适用于所有库:

  • 预先分配输出数组,而不是在循环中不断创建新数组
  • 重复利用 GPU 内存——使用内存池(CuPy 已内置内存池)
  • 使用固定(页锁定)主机内存,加快 CPU 与 GPU 之间的数据传输
  • 避免不必要的复制——尽可能使用原地操作
  • 使用流执行操作,以便计算与数据传输重叠进行
5. 需要注意的常见问题
  • 隐式回退到 CPU:某些操作会静默回退到 CPU。注意查看警告。
  • 同步开销:GPU 操作是异步的。调用 .get() 或 cp.asnumpy() 会强制同步。
  • dtype 不匹配:精度允许时,使用 float32 而非 float64——GPU 上 float32 的吞吐量高 2 到 32 倍。
  • 内核启动次数过多且规模过小:每次内核启动都有约 5-20us 的开销。尽可能合并操作。

代码转换模式

将现有 CPU 代码转换时,请应用以下模式:

NumPy 转 CuPy
# Before (CPU)
import numpy as np
a = np.random.rand(10_000_000)
b = np.fft.fft(a)
c = np.sort(b.real)

# After (GPU) — often just change the import
import cupy as cp
a = cp.random.rand(10_000_000)
b = cp.fft.fft(a)
c = cp.sort(b.real)
pandas 转 cuDF
# Before (CPU)
import pandas as pd
df = pd.read_parquet("large_data.parquet")
result = df.groupby("category")["value"].mean()

# After (GPU) — change the import
import cudf
df = cudf.read_parquet("large_data.parquet")
result = df.groupby("category")["value"].mean()

# Or zero-code-change: python -m cudf.pandas your_script.py
自定义循环转 Numba CUDA 内核
# Before (CPU) — slow Python loop
def process(data, out):
    for i in range(len(data)):
        out[i] = math.sin(data[i]) * math.exp(-data[i])

# After (GPU) — Numba kernel
from numba import cuda
import math

@cuda.jit
def process(data, out):
    i = cuda.grid(1)
    if i < data.size:
        out[i] = math.sin(data[i]) * math.exp(-data[i])

threads = 256
blocks = (len(data) + threads - 1) // threads
process[blocks, threads](d_data, d_out)
NetworkX 转 cuGraph
# Before (CPU)
import networkx as nx
G = nx.read_edgelist("edges.csv", delimiter=",", nodetype=int)
pr = nx.pagerank(G)
bc = nx.betweenness_centrality(G)

# After (GPU) — direct cuGraph API
import cugraph
import cudf
edges = cudf.read_csv("edges.csv", names=["src", "dst"], dtype=["int32", "int32"])
G = cugraph.Graph()
G.from_cudf_edgelist(edges, source="src", destination="dst")
pr = cugraph.pagerank(G)
bc = cugraph.betweenness_centrality(G)

# Or zero-code-change: NX_CUGRAPH_AUTOCONFIG=True python your_script.py
scikit-learn 转 cuML
# Before (CPU)
from sklearn.ensemble import RandomForestClassifier
from sklearn.preprocessing import StandardScaler
from sklearn.model_selection import train_test_split

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)
scaler = StandardScaler()
X_train = scaler.fit_transform(X_train)
model = RandomForestClassifier(n_estimators=100)
model.fit(X_train, y_train)

# After (GPU) — change the imports
from cuml.ensemble import RandomForestClassifier
from cuml.preprocessing import StandardScaler
from cuml.model_selection import train_test_split

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)
scaler = StandardScaler()
X_train = scaler.fit_transform(X_train)
model = RandomForestClassifier(n_estimators=100)
model.fit(X_train, y_train)

# Or zero-code-change: python -m cuml.accel your_script.py
仿真循环转 Warp 内核
# Before (CPU) — slow Python loop over particles
import numpy as np

def integrate(positions, velocities, forces, dt):
    for i in range(len(positions)):
        velocities[i] += forces[i] * dt
        positions[i] += velocities[i] * dt

# After (GPU) — Warp kernel, JIT-compiled to CUDA
import warp as wp

@wp.kernel
def integrate(positions: wp.array(dtype=wp.vec3),
              velocities: wp.array(dtype=wp.vec3),
              forces: wp.array(dtype=wp.vec3),
              dt: float):
    tid = wp.tid()
    velocities[tid] = velocities[tid] + forces[tid] * dt
    positions[tid] = positions[tid] + velocities[tid] * dt

wp.launch(integrate, dim=num_particles,
          inputs=[positions, velocities, forces, 0.01], device="cuda")
使用 KvikIO 将文件 I/O 转至 GPU
# Before — CPU staging (disk → CPU → GPU)
import numpy as np
import cupy as cp

data = np.fromfile("data.bin", dtype=np.float32)
gpu_data = cp.asarray(data)  # Extra copy through CPU memory

# After — direct to GPU (disk → GPU via GDS)
import cupy as cp
import kvikio

gpu_data = cp.empty(1_000_000, dtype=cp.float32)
with kvikio.CuFile("data.bin", "r") as f:
    f.read(gpu_data)  # Bypasses CPU memory with GPUDirect Storage

# Reading from S3 directly to GPU
with kvikio.RemoteFile.open_s3_url("s3://bucket/data.bin") as f:
    buf = cp.empty(f.nbytes() // 4, dtype=cp.float32)
    f.read(buf)
使用 cuxfilter 构建 GPU 加速仪表板
# Before — static matplotlib/seaborn plots, no interactivity
import pandas as pd
import matplotlib.pyplot as plt

df = pd.read_parquet("large_dataset.parquet")
fig, axes = plt.subplots(1, 2)
df.plot.scatter(x="feature1", y="feature2", ax=axes[0])
df["category"].value_counts().plot.bar(ax=axes[1])
plt.show()

# After (GPU) — interactive cross-filtering dashboard
import cudf
import cuxfilter

df = cudf.read_parquet("large_dataset.parquet")
cux_df = cuxfilter.DataFrame.from_dataframe(df)

scatter = cuxfilter.charts.scatter(x="feature1", y="feature2", pixel_shade_type="linear")
bar = cuxfilter.charts.bar("category")
slider = cuxfilter.charts.range_slider("value_col")

d = cux_df.dashboard(
    [scatter, bar],
    sidebar=[slider],
    layout=cuxfilter.layouts.feature_and_base,
    theme=cuxfilter.themes.rapids_dark,
    title="Interactive Explorer",
)
d.app()  # or d.show() for standalone web app
scikit-image 转 cuCIM
# Before (CPU)
from skimage.filters import gaussian, sobel, threshold_otsu
from skimage.morphology import binary_opening, disk
from skimage.measure import label, regionprops_table
import numpy as np

blurred = gaussian(image, sigma=3)
binary = blurred > threshold_otsu(blurred)
cleaned = binary_opening(binary, footprint=disk(3))
labels = label(cleaned)
props = regionprops_table(labels, image, properties=['area', 'centroid'])

# After (GPU) — change imports, wrap input with cp.asarray
from cucim.skimage.filters import gaussian, sobel, threshold_otsu
from cucim.skimage.morphology import binary_opening, disk
from cucim.skimage.measure import label, regionprops_table
import cupy as cp

image_gpu = cp.asarray(image)  # Transfer once
blurred = gaussian(image_gpu, sigma=3)
binary = blurred > threshold_otsu(blurred)
cleaned = binary_opening(binary, footprint=disk(3))
labels = label(cleaned)
props = regionprops_table(labels, image_gpu, properties=['area', 'centroid'])
GeoPandas 转 cuSpatial
# Before (CPU)
import geopandas as gpd
from shapely.geometry import Point

points = gpd.GeoDataFrame(geometry=[Point(x, y) for x, y in coords], crs="EPSG:4326")
polygons = gpd.read_file("regions.geojson")
joined = gpd.sjoin(points, polygons, predicate="within")

# After (GPU) — convert and use cuSpatial
import cuspatial
import cudf

points_cu = cuspatial.from_geopandas(points)
polygons_cu = cuspatial.from_geopandas(polygons)
joined = cuspatial.point_in_polygon(
    points_cu.geometry.x, points_cu.geometry.y,
    polygons_cu.geometry
)
Faiss/Annoy 转 cuVS
# Before (CPU) — Faiss
import faiss
import numpy as np

embeddings = np.random.rand(1_000_000, 128).astype(np.float32)
index = faiss.IndexFlatL2(128)
index.add(embeddings)
distances, neighbors = index.search(queries, k=10)

# After (GPU) — cuVS CAGRA (orders of magnitude faster)
import cupy as cp
from cuvs.neighbors import cagra

embeddings = cp.random.rand(1_000_000, 128, dtype=cp.float32)
index = cagra.build(cagra.IndexParams(), embeddings)
distances, neighbors = cagra.search(cagra.SearchParams(), index, queries, k=10)
scipy.sparse.linalg 转 RAFT
# Before (CPU)
import numpy as np
from scipy.sparse import random as sparse_random
from scipy.sparse.linalg import eigsh

A = sparse_random(10000, 10000, density=0.01, format="csr", dtype=np.float32)
A = A + A.T  # Make symmetric
eigenvalues, eigenvectors = eigsh(A, k=10, which="LM")

# After (GPU) — RAFT sparse eigensolver
import cupy as cp
import cupyx.scipy.sparse as sp_gpu
from pylibraft.sparse.linalg import eigsh as gpu_eigsh

A_gpu = sp_gpu.csr_matrix(A)  # Transfer to GPU
eigenvalues, eigenvectors = gpu_eigsh(A_gpu, k=10, which="LM")

重要说明

  • 始终处理 GPU 不可用的情况——提供 CPU 回退方案或清晰的错误消息
  • 根据 CPU 结果测试数值正确性(GPU 浮点结果可能因运算顺序不同而略有差异)
  • GPU 内存有限——对于大于 GPU 内存的数据集,可考虑分块处理,或使用 RAPIDS Dask 进行多 GPU 处理
  • CUDA Array Interface 可在 GPU 上的 CuPy、Numba、Warp、cuDF、cuML、cuGraph、cuVS、cuSpatial、KvikIO、PyTorch 和 JAX 数组之间实现零拷贝共享

参考文件

在编写任何 GPU 优化代码之前,请阅读相关参考文件:

文件阅读时机
references/cupy.md用户有 NumPy/SciPy 代码,或需要在 GPU 上执行数组操作
references/numba.md用户需要自定义 CUDA 内核、精细控制 GPU,或 GPU ufunc
references/cudf.md用户有 pandas 代码,或需要在 GPU 上执行 DataFrame 操作
references/cuml.md用户有 scikit-learn 代码,或需要在 GPU 上进行 ML 训练/推理/预处理
references/cugraph.md用户有 NetworkX 代码,或需要在 GPU 上进行图分析
references/warp.md用户需要 GPU 仿真、空间计算、网格/体积查询、可微编程或机器人技术
references/kvikio.md用户需要在 GPU 与其他设备之间进行高性能文件 IO、GPUDirect Storage、将 S3/HTTP 数据读入 GPU,或在 GPU 上使用 Zarr
references/cuxfilter.md用户需要 GPU 加速的交互式仪表板、交叉筛选或 EDA 可视化
references/cucim.md用户有 scikit-image 代码,或需要在 GPU 上进行图像处理、数字病理分析或 WSI 读取
references/cuvs.md用户需要在 GPU 上进行向量搜索、最近邻搜索、相似度搜索或 RAG 检索
references/cuspatial.md用户有 GeoPandas/shapely 代码,或需要进行空间连接、距离计算或轨迹分析
references/raft.md用户需要稀疏特征值求解器、设备内存管理或多 GPU 基元

编写代码前请先阅读对应的参考文件——其中包含各个库特有的详细 API 用法、优化技巧和注意事项。

评论

支持 Markdown