Cython编译优化:nogil/类型化内存视图/与NumPy互操作
请介绍Cython中与NumPy高效互操作的方案。说明类型化内存视图(Typed Memoryview)的语法和优势,与旧式ndarray缓冲区接口的对比。如何用nogil释放GIL实现多线程并行?给出一个使用Cython并行处理NumPy数组(prange)的示例。static typing后的Cython代码如何与纯Python代码混合使用?
回答
古法程序员
类型化内存视图(Memoryview):
# 声明2D double类型内存视图
cdef double[:, ::1] arr_view # ::1表示C-contiguous(连续,优化关键)
# 使用
arr_view[0, 0] = 3.14
与NumPy互操作:
# process_array.pyx
import numpy as np
cimport numpy as cnp
def process_ndarray(cnp.ndarray[double, ndim=2, mode='c'] arr):
"""旧式接口(Python 2兼容)"""
cdef Py_ssize_t i, j
for i in range(arr.shape[0]):
for j in range(arr.shape[1]):
arr[i, j] *= 2.0
def process_memoryview(double[:, ::1] arr):
"""新式接口(推荐)"""
cdef Py_ssize_t i, j
cdef Py_ssize_t rows = arr.shape[0]
cdef Py_ssize_t cols = arr.shape[1]
for i in range(rows):
for j in range(cols):
arr[i, j] *= 2.0
nogil + prange并行:
from cython.parallel import prange
cpdef double parallel_sum(double[:, ::1] arr) nogil:
cdef:
Py_ssize_t i, j
double total = 0
for i in prange(arr.shape[0], nogil=True, num_threads=4):
for j in range(arr.shape[1]):
total += arr[i, j]
return total
编译需OpenMP支持:
from setuptools import setup
from Cython.Build import cythonize
setup(
ext_modules=cythonize('parallel_process.pyx',
compiler_directives={'boundscheck': False, 'wraparound': False}),
define_macros=[('NPY_NO_DEPRECATED_API', 'NPY_1_7_API_VERSION')],
)
与纯Python混合使用:
- Cython编译后的
.so/.pyd文件直接import使用 - 仅性能关键部分用Cython,业务逻辑保持Python
- 可用
cpdef函数从Python调用 - Cython类可以继承Python类
.pxd文件定义接口(类似.h头文件),实现可变更
性能关键:boundscheck=False(取消边界检查)、wraparound=False(取消负索引)、initializedcheck=False可带来10-30%提升。