这个问题与:53641278密切相关。在该问题的评论中,@ead 建议使用问题 53619438 的 @DavidW 评论中的方法。
在我的问题中,用户向我提供了一个自定义Python函数(例如
times_two()
),我事先不知道它,我只知道参数和输出的数据类型(int
和int
)。我的工作是计算这个函数的大范围参数(例如1000
)。为了加快速度,我想使用 prange
,它需要 C
数据类型。
我想使用上述评论中的方法,我尝试这样做:
脚本.pyx
import ctypes
ctypedef int (* FuncPtr) (int tt)
ftype = ctypes.CFUNCTYPE(ctypes.c_int, ctypes.c_int)
cdef loop_over(FuncPtr cy_f_ptr):
cdef:
int i, s = 0
for i in range(1000):
s += cy_f_ptr(i)
return s
cpdef get_sum(func):
cdef FuncPtr cy_f_ptr = (<FuncPtr *> <size_t> ctypes.addressof(ftype(func)))[0]
s = loop_over(cy_f_ptr)
return s
设置.py
from distutils.core import setup
from Cython.Build import cythonize
setup(name="script", ext_modules=cythonize("script.pyx", compiler_directives={"language_level": "3"}))
终端
python setup.py build_ext -i
主.py
from script import get_sum
def times_two(x):
return x*2
print(get_sum(times_two))
运行时,出现以下错误:
Process finished with exit code -1073741819 (0xC0000005)
我的期望是代码将打印值
999000
。
我怀疑你的代码崩溃了,因为你扔掉了一个临时的:
你变了
f = ftype(func)
cdef FuncPtr cy_f_ptr = (<FuncPtr *> <size_t> ctypes.addressof(f))[0]
到
cdef FuncPtr cy_f_ptr = (<FuncPtr *> <size_t> ctypes.addressof(ftype(func)))[0]
只要函数指针存在,
ftype
对象就必须保持活动状态。在我的版本中是这样,而在您的版本中,ftype
对象几乎立即被销毁,这意味着函数指针一旦拥有它就无效。
但是,这里有一个基本问题:要调用 Python 函数,您必须持有 GIL,这意味着您不能使用
prange
来并行化它。你的函数指针方案稍微掩盖了这一点,但它仍然是正确的。
如果你在
with gil:
的调用周围引入 cy_f_ptr
,那么工作会是什么样的。仅当函数在内部释放 GIL 时,这才有意义(即最终调用优化的 C 代码本身)。 ctypes
可能会获取 GIL 本身 - 我不确定 - 但这仍然不会改变你无法像这样并行化 Python 代码的计算。