You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Cython在nogil块中调用带nogil声明的cpdef函数编译报错咨询

问题背景

该问题此前已有部分讨论,但我对现有解决方案并不满意,因此想咨询以下两个问题:

  1. 是否存在可以规避编写结构体并将其映射到扩展类这一操作的方法?
  2. 为什么会触发该编译错误?我调用的函数已经是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库的函数:

  1. 给utils库新增同名.pxd头文件,声明所有对外暴露的nogil函数的C签名。比如新建utils.pxd,内容如下:
cdef void blas_multiply(double[:,:] A, double[:] b, double[:] c) nogil
  1. 在主代码中用Cython导入逻辑替换普通导入:把from utils import blas_multiply改成from utils cimport blas_multiply。

改完后Cython编译主代码时就能识别到blas_multiply是不需要GIL的C级函数,不需要走Python对象调度,就能在nogil块里正常调用。如果utils库有大量同类函数,只需要批量把函数签名同步到utils.pxd里即可,不需要单独做结构体或者扩展类改造。


内容的提问来源于stack exchange,提问作者xan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.02 06:18:03