提问人:ffi23 提问时间:4/13/2019 最后编辑:ffi23 更新时间:4/24/2019 访问量:653
多线程 prange 循环抛出“double free or corruption (fasttop)”错误
Multithread prange loop throws "double free or corruption (fasttop)" error
问:
我对原来的问题做了一些改动。事实证明,正如评论中所建议的那样,malloc 部分实际上可能是问题所在。
我想在 Cython prange 循环中运行一个函数,如下面的代码所示。此代码引发“双重释放或损坏 (fasttop)”错误。
当我使用 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)
答:
我最初发现了一个不会导致您的问题但确实需要修复的问题(现在已在编辑后的代码中修复):Cython 无法知道是否引发了异常 - 在 C API 中,异常通过返回 来指示,但您的函数是 。请参阅文档的相关部分。您有两种选择:定义函数 to always check for an exception,或使用错误代码定义它:NULL
void
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.
实际问题是行。从 Numpy 数组到 memoryview 的转换确实需要某种锁定(即 GIL),或者存在引用计数的竞争条件。此争用条件会导致它在不应该释放时被释放,因此出现错误my_func(a, c)
解决方案是在循环外部生成一个 memoryview:a
cdef int[:] a_mview = a
# then inside the prange loop
my_func(a_mview, c).
在并行部分中使用 memoryview 是可以的,但这只是初始创建的问题。我认为 Cython 在编译时没有将其标记为错误是一个错误,可能值得报告。
评论
c
num_threads=2
cdef int [:] arr_v = arr
cdef int my_func(int[:] arr_cy, int c) nogil except -1:
going
def going(int [:] a):
@DavidW的答案是可以的,但并不是问题的完整答案。经过一番摆弄,我找到了我想要的东西:我需要为内存视图使用指针,如cython文档上的通过指针从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)
评论
malloc
free