我对原始问题做了一些更改。事实证明, malloc 部分实际上可能是问题所在,如评论中所建议。
我想在Cython prange循环中运行一个函数,如下面的代码所示。此代码引发“ 双重释放或损坏(快速停止)”错误。
当我使用prange标志“ num_threads = 1”运行代码时,一切都很好。 我了解我的代码可能不是线程安全的,但我不明白为什么。
import numpy as np
cimport numpy as np
cimport cython
from cython.parallel import prange
from libc.stdlib cimport malloc, free
cdef int my_func(int[:] arr_cy, int c) nogil except -1:
cdef int i
cdef int *arr_to_process = <int *>malloc(c * sizeof(int))
if not arr_to_process:
with gil:
raise MemoryError()
try:
for i in range(c):
arr_to_process[i] = 1
finally:
free(arr_to_process)
return 0
def going(a):
cdef int c
for c in prange(100000, nogil=True, num_threads=2):
my_func(a, c)
def get_going(iterations):
arr = np.arange(1000000, dtype=np.intc)
cdef int [:] arr_v = arr
for a in range(iterations):
print('iter %i' %a)
going(arr_v)
如果我以足够的迭代次数(例如30)运行get_going(iterations)
,则始终会引发错误。我觉得我很笨,但我不明白。谢谢您的帮助。
答案 0 :(得分:1)
我最初确定了一个不会引起您问题的问题,但确实需要修复(现在已在编辑的代码中修复):Cython无法知道已引发异常-在C API中,异常是通过返回NULL
来表示,但您的函数是void
。参见the relevant bit of the documentation。您有两个选择:用except *
定义函数以始终检查异常,或用错误代码定义它:
cdef int my_func(int[:] arr_cy, int c) nogil except 1:
# ... code goes here
return 0 # indicate no error
Cython will automatically use this when you raise an exception.
实际问题是第my_func(a, c)
行。从Numpy数组到memoryview的转换确实需要某种锁定(即GIL),或者存在带有引用计数的竞争条件。这种竞争状态导致它在不应该释放时被释放,因此出现错误
解决方案是在循环外生成a
的内存视图:
cdef int[:] a_mview = a
# then inside the prange loop
my_func(a_mview, c).
在并行部分中可以使用memoryview,但这只是最初的创建而已。我认为Cython在编译时未将其标记为错误是一个错误,可能值得reporting。
答案 1 :(得分:0)
@DavidW的回答是可以的,但是它不能完全解决问题。经过一番摆弄之后,我找到了我要寻找的东西:我需要对内存视图使用指针,如cython docs上的通过指针从C函数传递数据部分所述。这是完整的工作代码。
cdef int my_func(int arr_cy[], int c) nogil except -1:
cdef int i
cdef int *arr_to_process = <int *>malloc(c * sizeof(int))
if not arr_to_process:
with gil:
raise MemoryError()
try:
for i in range(c):
arr_to_process[i] = 1
finally:
free(arr_to_process)
return 0
def going(a):
cdef int c
cdef int [:1] arr_v = a
for c in prange(100000, nogil=True, num_threads=2):
my_func(&arr_v[0], c)
def get_going(it):
arr = np.arange(1000000, dtype=np.intc)
for ii in range(it):
print('iter %i' %ii)
going(arr)