scientific-agent-skills 之 CuPy 全面指南:将 NumPy/SciPy 科学计算无缝迁移到 NVIDIA GPU scientific-agent-skills 之 CuPy 全面指南将 NumPy/SciPy 科学计算无缝迁移到 NVIDIA GPU【免费下载链接】scientific-agent-skillsTurn any AI agent into an AI Scientist. The #1 Agent Skills library for science, used by 190,000 scientists worldwide. 165 ready-to-use validated skills plus 100 scientific databases covering biology, chemistry, medicine, and drug discovery. Compatible with Cursor, Claude Code, Codex, Pi, Antigravity, and the open Agent Skills standard.项目地址: https://gitcode.com/GitHub_Trending/cl/scientific-agent-skillsCuPy 是一个与 NumPy/SciPy 完全兼容的 GPU 加速数组库它封装了 NVIDIA 优化后的底层数学库cuBLAS、cuFFT、cuSOLVER、cuSPARSE、cuRAND使标准数组运算开箱即用即高度调优。在 scientific-agent-skills 仓库的optimize-for-gpu技能中CuPy 被定位为处理 NumPy/SciPy 型数组计算的首选 GPU 加速路径。阅读本文后你将掌握从安装选型、改 import 即可上手的 Drop-in 替换到自定义 CUDA Kernel、内核融合、内存池管理、流式异步、多 GPU 与性能调优的完整实战能力。本文以 cupy.md 为骨架展开并结合该技能目录下的 SKILL.md、installation.md、decision_framework.md 与 code_transformation_patterns.md 进行源码级佐证与扩展。一、安装与运行环境验证版本选型与包名CuPy 针对不同 CUDA 版本发布独立的 pip 轮子必须按本机 CUDA 版本选择正确的包名。根据 installation.md 的约定本仓库统一使用uv add安装依赖若用户项目已配置其他包管理器则遵循项目原有习惯uv add cupy-cuda12x14.1.* # For CUDA 12.x uv add cupy-cuda13x14.1.* # For CUDA 13.x版本前提以当前仓库文档为准CuPy v14 要求 CUDA 12.0、Python 3.10、NumPy 2.0并遵循 NumPy 2 的类型提升规则NEP 50同时支持自由线程free-threadedPython。[ctk]extra例如cupy-cuda13x[ctk]会从 PyPI 拉取所需的 CUDA 运行时组件因此本机只需要预装 NVIDIA 驱动无需完整安装 CUDA Toolkit。安装后验证import cupy as cp print(cp.cuda.runtime.getDeviceCount()) # 1 means GPU is available print(cp.show_config()) # Full environment infocp.cuda.runtime.getDeviceCount()返回可用 GPU 数量大于等于 1 即代表 GPU 可用cp.show_config()输出完整的运行时环境信息包括 CUDA 版本、驱动版本、cuBLAS/cuFFT 等依赖库的版本便于排查环境不一致问题。二、Drop-In 替换模式最快的 GPU 加速起点对于纯 NumPy 代码最快的 GPU 加速方式就是改 import。CuPy 的 API 设计与 NumPy 高度对齐绝大多数 NumPy 代码只需把import numpy as np换成import cupy as cp即可运行在 GPU 上# 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) import cupy as cp a cp.random.rand(10_000_000) b cp.fft.fft(a) c cp.sort(b.real)这一转换模式在 code_transformation_patterns.md 中被列为NumPy to CuPy的标准范式并强调often just change the import。CPU 与 GPU 之间的数据传输# NumPy → CuPy (CPU → GPU) gpu_array cp.asarray(numpy_array) # Zero-copy if already on current device gpu_array cp.array(numpy_array) # Always copies # CuPy → NumPy (GPU → CPU) cpu_array cp.asnumpy(gpu_array) # Copy to CPU cpu_array gpu_array.get() # Same thing注意两种入方向转换的区别cp.asarray()在数组已位于当前设备时为零拷贝而cp.array()始终执行拷贝。无论哪种方式每次跨设备转换都会触发 PCIe 数据传输与隐式同步是性能敏感路径上应极力避免的操作。编写 CPU/GPU 无关的通用代码当一段算法希望同时支持 NumPy 与 CuPy 输入时使用cp.get_array_module()动态获取输入数组对应的模块def normalize(x): xp cp.get_array_module(x) # Returns cupy or numpy depending on input return x / xp.linalg.norm(x) # Works with both NumPy and CuPy arrays normalize(numpy_array) # Runs on CPU normalize(cupy_array) # Runs on GPU此外CuPy 数组实现了__array_ufunc__与__array_function__协议NumPy 1.17因此当向 NumPy 函数传入 CuPy 数组时NumPy 会自动将调用分派给 CuPy 执行并返回 CuPy 数组——这正是互操作章节中np.sum(cupy_array)能直接工作的底层原理。三、核心 APIcupy.ndarraycupy.ndarray镜像了numpy.ndarray的全部核心属性——shape、dtype、ndim、size、strides、T并额外提供device属性用于标识数组所在的 GPU 编号。最重要的心智模型cupy.ndarray与numpy.ndarray之间不存在隐式转换每一次转换都伴随一次 host-device 数据传输。数组创建cp.empty((1000, 1000), dtypecp.float32) cp.zeros((1000,), dtypecp.float64) cp.ones((512, 512), dtypecp.float32) cp.full((100,), fill_value3.14, dtypecp.float32) cp.arange(0, 100, 0.1) cp.linspace(0, 1, 1000) cp.eye(100) cp.random.rand(1000, 1000) # Uniform [0, 1) cp.random.randn(1000, 1000) # Standard normal cp.random.default_rng(42).normal(0, 1, 1000) # Generator API与 NumPy 的一个重要区别CuPy 的随机函数支持dtype参数float32/float64而 NumPy 总是返回 float64。当不需要双精度时明确使用dtypecp.float32可显著提升吞吐并降低显存占用。四、支持的操作全景CuPy 实现了 NumPy 的大部分功能以及 SciPy 的很大一部分且全部经过 GPU 加速。以下是原文档列出的完整能力清单按功能域组织数组数学与逐元素运算sin、cos、tan、exp、log、log2、log10、sqrt、square、abs、power、add、subtract、multiply、divide、mod、clip、sign、ceil、floor、round、maximum、minimum归约Reductionssum、prod、mean、std、var、min、max、argmin、argmax、cumsum、cumprod、any、all、nansum、nanmean、nanstd、nanvar线性代数cupy.linalg由 cuBLAS/cuSOLVER 驱动dot、matmul、运算符、tensordot、einsum、inner、outer、cholesky、qr、svd、eig、eigh、eigvalsh、norm、solve、inv、pinv、lstsq、det、slogdet、matrix_rank、matrix_powerFFTcupy.fft由 cuFFT 驱动fft、ifft、fft2、ifft2、fftn、ifftn、rfft、irfft、rfft2、irfft2、rfftn、irfftn、fftfreq、rfftfreq、fftshift、ifftshift排序与搜索sort、argsort、partition、argpartition、argmin、argmax、where、nonzero、unique、searchsorted数组操作reshape、ravel、flatten、transpose、swapaxes、concatenate、stack、vstack、hstack、dstack、split、hsplit、vsplit、tile、repeat、pad、flip、fliplr、flipud、roll、rot90、broadcast_to、expand_dims、squeeze稀疏矩阵cupyx.scipy.sparse支持 CSR、CSC、COO 三种格式包含矩阵-向量乘、矩阵-矩阵乘及格式间转换底层由 cuSPARSE 驱动。CuPy v14 新增了对 64 位维度和非零计数的超大规模稀疏矩阵支持。信号处理cupyx.scipy.signal卷积、相关、滤波、窗函数。特殊函数cupyx.scipy.special贝塞尔函数、误差函数、伽马函数等。统计mean、median、std、var、percentile、quantile、corrcoef、cov、histogram、bincount、digitize五、自定义 Kernel从简单到强大的四级方案当内置操作无法满足需求时CuPy 按从最简单到最强大的顺序提供了多种编写自定义 GPU 代码的方式。5.1 ElementwiseKernel — 自定义逐元素操作CuPy 自动处理索引与广播你只需用 C 编写单元素逻辑squared_diff cp.ElementwiseKernel( float32 x, float32 y, # Input params float32 z, # Output params z (x - y) * (x - y), # Per-element operation (C code) squared_diff # Kernel name ) result squared_diff(a, b) # Broadcasting works automatically类型泛化 Kernel使用单字母类型占位符相同字母代表相同类型调用时根据实参自动解析generic_squared_diff cp.ElementwiseKernel( T x, T y, T z, z (x - y) * (x - y), generic_squared_diff ) # Works with float32, float64, etc. — type inferred from inputsRaw 索引参数前缀raw可关闭自动索引此时使用i作为循环索引适合实现访问相邻元素stencil的计算# Access neighbors — raw disables auto-indexing so you can index manually stencil cp.ElementwiseKernel( raw T x, T y, y (x[i 0 ? i-1 : 0] x[i] x[i _ind.size()-1 ? i1 : _ind.size()-1]) / 3, stencil_1d )5.2 ReductionKernel — 自定义归约归约 Kernel 由四部分组成映射每个元素、两两归约、后处理结果、以及恒等元l2norm cp.ReductionKernel( T x, # Input T y, # Output x * x, # Map: square each element a b, # Reduce: sum pairs (a, b are the binary operands) y sqrt(a), # Post-map: sqrt of final sum 0, # Identity element l2norm # Kernel name ) norm l2norm(array) # Full reduction → scalar norms l2norm(matrix, axis1) # Reduce along axis → vector5.3 RawKernel — 完整 CUDA C/C需要完全控制 grid、block、shared memory 时直接编写原始 CUDAkernel_code r extern C __global__ void vector_add(const float* a, const float* b, float* c, int n) { int tid blockDim.x * blockIdx.x threadIdx.x; if (tid n) { c[tid] a[tid] b[tid]; } } vector_add cp.RawKernel(kernel_code, vector_add) n 1_000_000 a cp.random.rand(n, dtypecp.float32) b cp.random.rand(n, dtypecp.float32) c cp.zeros(n, dtypecp.float32) threads 256 blocks (n threads - 1) // threads vector_add((blocks,), (threads,), (a, b, c, n)) # (grid, block, args)RawKernel 的重要注意事项忽略数组视图/步长matrix.T会被当作原始连续布局处理步长必须自行处理使用extern C防止 C 名称重整name mangling处理复数时需要#include cupy/complex.cuh编译后的二进制缓存在~/.cupy/kernel_cache。CuPy dtype 与 CUDA 类型映射表CuPy dtypeCUDA typefloat16halffloat32floatfloat64doubleint32intint64long longcomplex64complexfloatcomplex128complexdouble5.4 RawModule — 大型 CUDA 代码库适用于包含多个 Kernel 的 CUDA 文件或预编译二进制module cp.RawModule(codecuda_source) # From source string module cp.RawModule(pathkernels.cu) # From file module cp.RawModule(pathkernels.cubin) # From precompiled kernel module.get_function(my_kernel) kernel((blocks,), (threads,), (args...))5.5 JIT Kernelcupyx.jit.rawkernel— 用 Python 语法写 CUDA Kernel以 Python 语法编写 CUDA 风格内核而不是 Ccupyx.jit.rawkernel() def my_kernel(x, y, size): tid cupyx.jit.grid(1) if tid size: y[tid] x[tid] * 2.0 my_kernelblocks, threads可用的 JIT 原语cupyx.jit.threadIdx、blockIdx、blockDim、gridDimcupyx.jit.grid(ndim)、gridsize(ndim)cupyx.jit.syncthreads()、syncwarp()cupyx.jit.shared_memory(dtype, size)cupyx.jit.atomic_add/min/max/and/or/xor(array, index, value)Warp 级洗牌shfl_sync、shfl_up_sync、shfl_down_sync、shfl_xor_sync限制JIT Kernel 需要访问源码因此无法在 Python REPL 中工作必须从.py文件运行。六、Kernel 融合Kernel Fusion将多个逐元素操作合并为单次 Kernel 启动可消除中间数组并降低内核启动开销cp.fuse() def fused_op(x, y): return cp.sqrt((x - y) ** 2 1.0) # This compiles into ONE kernel instead of multiple result fused_op(a, b)限制cp.fuse()只能融合逐元素操作和简单的归约操作不支持matmul、reshape、索引等操作。七、内存管理内存池默认行为CuPy 默认使用内存池这对性能至关重要。池会缓存释放的 GPU 内存以便复用避免昂贵的cudaMalloc/cudaFree调用以及它们引发的隐式同步。关键认知数组离开作用域时内存并不会释放回操作系统而是归还给内存池。因此nvidia-smi中显示的内存占用保持不降是预期行为而非内存泄漏。mempool cp.get_default_memory_pool() mempool.used_bytes() # Currently allocated by CuPy arrays mempool.total_bytes() # Total held by pool (including free blocks) mempool.free_all_blocks() # Release all unused memory back to OS pinned_mempool cp.get_default_pinned_memory_pool() pinned_mempool.free_all_blocks()限制 GPU 内存上限mempool cp.get_default_memory_pool() with cp.cuda.Device(0): mempool.set_limit(size4 * 1024**3) # 4 GiB limit for GPU 0或者在import cupy之前通过环境变量设置export CUPY_GPU_MEMORY_LIMIT50% # Percentage of total GPU memory export CUPY_GPU_MEMORY_LIMIT4294967296 # Bytes托管统一内存数据可在 CPU 与 GPU 之间自动迁移适用于数据放不进 GPU 显存的场景cp.cuda.set_allocator(cp.cuda.MemoryPool(cp.cuda.malloc_managed).malloc)页锁定Pinned内存加速传输# High-level API pinned_array cupyx.empty_pinned((1000,), dtypenp.float32) pinned_array cupyx.zeros_pinned((1000,), dtypenp.float32) # These are NumPy arrays backed by page-locked memory — transfers to GPU are faster页锁定内存是底层为内存页锁定page-locked的 NumPy 数组到 GPU 的传输速度更快。禁用内存池cp.cuda.set_allocator(None) # Disable device pool cp.cuda.set_pinned_memory_allocator(None) # Disable pinned pool必须在任何 CuPy 操作之前执行。与 RMMRAPIDS Memory Manager协同当 CuPy 与 cuDF/RAPIDS 同时使用时应统一到同一个分配器import rmm from rmm.allocators.cupy import rmm_cupy_allocator rmm.reinitialize(pool_allocatorTrue) cp.cuda.set_allocator(rmm_cupy_allocator)八、流Streams与异步操作Stream 使得计算与数据传输可以重叠并允许并发执行多个操作。stream cp.cuda.Stream() # Context manager style with stream: d_data cp.asarray(host_data) # H→D transfer on this stream result cp.sum(d_data) # Kernel on this stream # Operations enqueued but may not be complete here stream.synchronize() # Wait for all operations on this stream多 Stream 重叠s1 cp.cuda.Stream() s2 cp.cuda.Stream() with s1: d_a cp.asarray(data_a) result_a cp.fft.fft(d_a) with s2: d_b cp.asarray(data_b) # Overlaps with s1s FFT result_b cp.fft.fft(d_b) cp.cuda.Device().synchronize() # Wait for all streams用 Event 计时start cp.cuda.Event() end cp.cuda.Event() start.record() # ... GPU operations ... end.record() end.synchronize() elapsed_ms cp.cuda.get_elapsed_time(start, end)每线程默认流export CUPY_CUDA_PER_THREAD_DEFAULT_STREAM1在多线程应用中启用每线程默认流per-thread default stream可获得更好的并发性。九、多 GPU# Set current device cp.cuda.Device(0).use() # Context manager with cp.cuda.Device(1): x cp.array([1, 2, 3]) # Allocated on GPU 1 # Check which device an array is on print(x.device) # Device 1跨设备操作在 GPU 拓扑支持 P2Ppeer-to-peer内存访问时可能直接可用否则请使用cp.asarray()显式在设备间传输数组。按设备设置内存上限mempool cp.get_default_memory_pool() with cp.cuda.Device(0): mempool.set_limit(size4 * 1024**3) with cp.cuda.Device(1): mempool.set_limit(size4 * 1024**3)十、性能优化正确的基准测试关键第一步绝对不要用time.perf_counter()或%timeit评测 GPU 代码——它们只测量 CPU 时间而非 GPU 执行时间。CuPy 操作是异步的CPU 计时器测到的只是入队耗时。这也与 SKILL.md 中GPU 工作异步执行CPU 计时器测量的是入队时间的结论一致from cupyx.profiler import benchmark result benchmark(my_function, (arg1, arg2), n_repeat100, n_warmup10) print(result) # Shows CPU and GPU elapsed times with statistics在 IPython/Jupyter 中%load_ext cupyx.profiler %gpu_timeit my_function(args)一次性开销上下文初始化首次调用 CuPy 可能需要 1–5 秒CUDA 上下文创建这是一次性的Kernel JIT 编译任何操作的首次调用都会触发即时内核编译结果缓存在~/.cupy/kernel_cache。在 CI/CD 流水线中应持久化该目录。CUB 与 cuTENSOR 加速# CuPy v11 uses CUB by default export CUPY_ACCELERATORScub # CUB only (default) export CUPY_ACCELERATORScub,cutensor # Both (requires cuTENSOR installed)CUB 加速的典型操作包括归约sum、prod、amin、amax、argmin、argmax、包含式扫描cumsum、直方图、稀疏矩阵-向量乘以及ReductionKernel。收益取决于 dtype、shape、axis 与硬件务必对目标操作实测。cuTENSOR 加速二元逐元素 ufunc、归约、张量收缩。关键优化策略数值契约允许时优先使用 float32 而非 float64。吞吐差异取决于 GPU 架构务必在目标设备上验证精度并基准测试。最小化 CPU-GPU 传输。每次cp.asnumpy()/.get()都会触发同步和 PCIe 传输尽量让数据留在 GPU 上。使用内核融合。cp.fuse()将多个逐元素操作合并为一个内核消除中间数组。批量操作。少而大的操作通常优于多而小的操作请以目标系统实测的启动开销为准。预分配输出数组。在 ufunc 中使用out参数避免重复分配cp.add(a, b, outresult) # Writes into existing array使用就地操作。a b可避免分配新数组。使用 Stream 重叠计算与数据传输。使用 NVTX 标记配合 Nsight Systems 分析with cupyx.profiler.time_range(my_operation, color_id0): result heavy_computation()Kernel 方案决策树能用 NumPy 操作表达→ 使用 CuPy 内置函数开发最快性能往往也最好多个链式逐元素操作→ 使用cp.fuse()带广播的自定义逐元素→ 使用ElementwiseKernel自定义归约→ 使用ReductionKernel需要完整 grid/block/shared memory 控制→ 使用RawKernel或cupyx.jit.rawkernel大型 CUDA 代码库→ 使用RawModule决策树背后还有一条更上层的原则根据 decision_framework.md 与 SKILL.md应优先选择维护良好的库实现而不是自定义 Kernel——先用 CuPy 这类库方案只有 profiling 证明没有合适的库实现时才编写自定义内核并把 GPU 加速当作基于证据的优化而非自动重写。十一、与其他 GPU 库的互操作CuPy 通过CUDA Array Interface与DLPack协议与其他 GPU 库实现零拷贝数据共享。NumPy# NumPy functions auto-dispatch to CuPy (NumPy 1.17) import numpy as np result np.sum(cupy_array) # Dispatches to CuPy, returns CuPy arrayNumbafrom numba import cuda cuda.jit def numba_kernel(x, y): i cuda.grid(1) if i x.shape[0]: y[i] x[i] * 2 # CuPy arrays pass directly to Numba kernels — zero copy a cp.arange(1000, dtypecp.float32) b cp.zeros_like(a) numba_kernel4, 256PyTorchimport torch # CuPy → PyTorch (zero copy via CUDA Array Interface) cupy_array cp.array([1.0, 2.0, 3.0], dtypecp.float32) torch_tensor torch.as_tensor(cupy_array, devicecuda) # PyTorch → CuPy (zero copy) cupy_array cp.asarray(torch_tensor) # Via DLPack (also zero copy) cupy_array cp.from_dlpack(torch_tensor) torch_tensor torch.from_dlpack(cupy_array)cuDFimport cudf # cuDF → CuPy arr df.to_cupy() arr cp.asarray(df[column]) # CuPy → cuDF df cudf.DataFrame(cupy_array) s cudf.Series(cupy_array)原始指针互操作# Export pointer ptr cupy_array.data.ptr # Raw device pointer as int # Import foreign pointer mem cp.cuda.UnownedMemory(ptr, size_bytes, ownerowner_obj) memptr cp.cuda.MemoryPointer(mem, offset0) arr cp.ndarray(shape, dtype, memptrmemptr)需要强调的是SKILL.md 中的Important Notes尽管 CUDA Array Interface / DLPack 支持零拷贝交换使用前仍要验证 device、dtype、连续性contiguity、所有权与 stream 语义而不是想当然地认为每次转换都是免费的。十二、与 NumPy 的关键差异易踩坑的行为差异以下行为差异是迁移时最常见的 bug 来源归约返回 0 维数组而非标量。cp.sum(a)返回 0 维cupy.ndarray而不是 Python float。这是为了避免隐式 GPU-CPU 同步。需要标量时使用.item()。越界索引静默环绕。NumPy 会抛出IndexError而 CuPy 不做报错直接环绕wrap around。赋值中重复索引的结果未定义。a[[0, 0]] [1, 2]——NumPy 存最后的值CuPy 存未定义的值GPU 竞争条件。浮点到整数的边界转换不同。负 float 转无符号整型、无穷大转整型的结果与 NumPy 不同。不支持字符串/对象 dtype。CuPy 仅支持数值类型不支持带字符串字段的结构化数组。CuPy ufunc 只接受 CuPy 数组。与 NumPy ufunc 不同CuPy ufunc 不接受列表或 NumPy 数组需先转换。随机种子数组会被哈希。数组种子产生的熵低于 NumPy 的做法。十三、常见陷阱清单用 CPU 计时器测量 GPU。GPU 操作是异步的time.perf_counter()测到的只是入队时间。始终使用cupyx.profiler.benchmark()。不必要的往返传输。每次cp.asnumpy()/.get()都会同步 GPU 并跨 PCIe 拷贝数据应重构代码让数据留在 GPU 上。把内存池当内存泄漏。内存池缓存了释放的块nvidia-smi会显示为已分配。用mempool.free_all_blocks()释放。首次调用延迟。CUDA 上下文初始化 Kernel JIT 编译所致基准测试前先预热。混用设备。未显式传输就在 GPU 1 上使用 GPU 0 的数组可能失败或变慢。RawKernel 忽略视图。转置或切片后的数组传给 RawKernel 时按原始连续布局处理必须手动处理步长。读取结果前忘记synchronize()。将数据传回 CPU 或在非 CuPy 代码中使用时务必确保 GPU 已完成。十四、环境变量速查表VariablePurposeCUPY_ACCELERATORSBackend list:cub,cutensor(default:cubfor v11)CUPY_CACHE_DIRKernel cache directory (default:~/.cupy/kernel_cache)CUPY_GPU_MEMORY_LIMITGPU memory limit (bytes or50%)CUPY_CACHE_SAVE_CUDA_SOURCESet1to dump kernel source for profilingCUPY_CUDA_PER_THREAD_DEFAULT_STREAMSet1for per-thread default streams结语CuPy 是 optimize-for-gpu 技能体系中处理 NumPy/SciPy 型数组计算的首选加速路径其价值在于以最小的改动获得 GPU 级性能改 import 即可启动迁移内置 API 覆盖 NumPy 主体与 SciPy 大量功能当需要更精细的控制时ElementwiseKernel → ReductionKernel → RawKernel/RawModule → JIT Kernel 的四级梯度提供了从逐元素逻辑到完整 CUDA C/C 代码库的完整演进路径。真正的性能工程始于正确的测量GPU 事件计时而非 CPU 计时器、忠于数据在设备上的驻留策略并以 CPU 结果为基准做语义验证——这正是本技能所强调的evidence-driven optimization核心理念。如需进一步了解 CuPy 在整个 GPU 优化体系中的定位何时选它、何时选 Numba/Warp/cuDF 等其他库可继续阅读 decision_framework.md 与 code_transformation_patterns.md安装与 CUDA 版本选择的完整矩阵见 installation.md。【免费下载链接】scientific-agent-skillsTurn any AI agent into an AI Scientist. The #1 Agent Skills library for science, used by 190,000 scientists worldwide. 165 ready-to-use validated skills plus 100 scientific databases covering biology, chemistry, medicine, and drug discovery. Compatible with Cursor, Claude Code, Codex, Pi, Antigravity, and the open Agent Skills standard.项目地址: https://gitcode.com/GitHub_Trending/cl/scientific-agent-skills创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考