Cython在nogil块中调用带nogil声明的cpdef函数编译报错咨询
问题背景
该问题此前已有部分讨论,但我对现有解决方案并不满意,因此想咨询以下两个问题:
- 是否存在可以规避编写结构体并将其映射到扩展类这一操作的方法?
- 为什么会触发该编译错误?我调用的函数已经是
cdef类型且标注了nogil修饰符,在持有GIL的环境中调用可以正常编译通过。
第一个问题对我而言非常重要,因为我后续会调用utils库中的大量函数,如果都按照现有公开方案描述的方法编写代码,时间成本会极高。
我的代码(iPython notebook环境)
%%cython import numpy as np cimport numpy as np mingw_setup_args={'script_args': ["--compiler=mingw32", "--cython-cplus"], 'include_dirs': np.get_include()} import pyximport; pyximport.install(setup_args=mingw_setup_args, reload_support=True, language_level=3) from utils import blas_multiply D_TYPE = np.dtype(np.float64) cdef double[:,:] A = np.random.randn(100, 10, dtype=D_TYPE) cdef double[:] b = np.random.randn(10, dtype=D_TYPE) cdef double[:] out = np.zeros((A.shape[0], ), dtype=D_TYPE) # blas_multiply(A, b, out) # 持有GIL时调用正常 with nogil: blas_multiply(A, b, out)
utils.pyx文件内容
#!python # distutils: language=c++ # cython: language_level=3 cimport cython import numpy as np cimport numpy as np cimport scipy.linalg.cython_blas as blas @cython.cdivision(True) @cython.boundscheck(False) @cython.wraparound(False) @cython.nonecheck(False) cpdef void blas_multiply(double[:,:] A, double[:] b, double[:] c) nogil: """ 调用BLAS的dgemv接口,计算逻辑为 y = alpha * trans(A) + beta * y A: m行k列 b: k行1列 c: k行1列 """ cdef int m = A.shape[0] cdef int k = A.shape[1] cdef int n = 1 cdef double alpha = 1.0 cdef double beta = 0.0 # F序连续数组用该调用 # blas.dgemv("N", &m, &k, &alpha, &A[0, 0], &m, &b[0], &n, &beta, &c[0], &n) # C序连续数组用该调用 blas.dgemv("T", &k, &m, &alpha, &A[0, 0], &k, &b[0], &n, &beta, &c[0], &n)
编译报错信息
Error compiling Cython file: ------------------------------------------------------------ ... # blas_multiply(A, b, out) # print(out) with nogil: blas_multiply(A, b, out) ^ ------------------------------------------------------------ C:\Users\...\.ipython\cython\_cython_magic_6b53bffa20f1e5bcc572f847b4b26a3f.pyx:22:17: Discarding owned Python object not allowed without gil Error compiling Cython file: ------------------------------------------------------------ ... # blas_multiply(A, b, out) # print(out) with nogil: blas_multiply(A, b, out) ^ ------------------------------------------------------------ C:\Users\...\.ipython\cython\_cython_magic_6b53bffa20f1e5bcc572f847b4b26a3f.pyx:22:17: Calling gil-requiring function not allowed without gil Error compiling Cython file: ------------------------------------------------------------ ... # blas_multiply(A, b, out) # print(out) with nogil: blas_multiply(A, b, out) ^ ------------------------------------------------------------ C:\Users\...\.ipython\cython\_cython_magic_6b53bffa20f1e5bcc572f847b4b26a3f.pyx:22:4: Accessing Python global or builtin not allowed without gil Error compiling Cython file: ------------------------------------------------------------ ... # blas_multiply(A, b, out) # print(out) with nogil: blas_multiply(A, b, out) ^ ------------------------------------------------------------ C:\Users\...\.ipython\cython\_cython_magic_6b53bffa20f1e5bcc572f847b4b26a3f.pyx:22:17: Constructing Python tuple not allowed without gil Error compiling Cython file: ------------------------------------------------------------ ... # blas_multiply(A, b, out) # print(out) with nogil: blas_multiply(A, b, out) ^ ------------------------------------------------------------ C:\Users\...\.ipython\cython\_cython_magic_6b53bffa20f1e5bcc572f847b4b26a3f.pyx:22:18: Converting to Python object not allowed without gil Error compiling Cython file: ------------------------------------------------------------ ... # blas_multiply(A, b, out) # print(out) with nogil: blas_multiply(A, b, out) ^ ------------------------------------------------------------ C:\Users\...\.ipython\cython\_cython_magic_6b53bffa20f1e5bcc572f847b4b26a3f.pyx:22:21: Converting to Python object not allowed without gil Error compiling Cython file: ------------------------------------------------------------ ... # blas_multiply(A, b, out) # print(out) with nogil: blas_multiply(A, b, out) ^ ------------------------------------------------------------ C:\Users\...\.ipython\cython\_cython_magic_6b53bffa20f1e5bcc572f847b4b26a3f.pyx:22:24: Converting to Python object not allowed without gil
问题解答
编译错误原因
报错的核心是你在主代码中用普通Python导入逻辑from utils import blas_multiply引入的是Python层面的函数对象,Cython编译主代码时看不到这个函数的C级签名,只会把它当成普通Python可调用对象处理,调用时需要走Python对象调度流程,自然要求持有GIL。
你在有GIL的环境下调用正常,是因为Python函数调度本来就需要GIL,刚好符合运行要求。
无需编写结构体的解决方案
完全不需要编写结构体映射扩展类,只需要两步改动就能适配所有utils库的函数:
- 给
utils库新增同名.pxd头文件,声明所有对外暴露的nogil函数的C签名。比如新建utils.pxd,内容如下:
cdef void blas_multiply(double[:,:] A, double[:] b, double[:] c) nogil
- 在主代码中用Cython导入逻辑替换普通导入:把
from utils import blas_multiply改成from utils cimport blas_multiply。
改完后Cython编译主代码时就能识别到blas_multiply是不需要GIL的C级函数,不需要走Python对象调度,就能在nogil块里正常调用。如果utils库有大量同类函数,只需要批量把函数签名同步到utils.pxd里即可,不需要单独做结构体或者扩展类改造。
内容的提问来源于stack exchange,提问作者xan
相关产品推荐
相关产品推荐

