Я внес несколько изменений в исходный вопрос. Оказывается, что часть malloc , вероятно, является проблемой, как предлагается в комментариях.

Я хочу, чтобы запустить функцию в цикле pthon Cython, как в коде ниже. Этот код выдает ошибку « двойное освобождение или повреждение (fasttop) ».

Когда я запускаю код с оранжевым флагом «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)

Если я запускаю get_going(iterations) с достаточным количеством итераций, например 30, это всегда выдает ошибку. Я чувствую, что я очень тупой, но я не понимаю. Спасибо за помощь.

0
ffi23 12 Апр 2019 в 20:43

2 ответа

Лучший ответ

Ответ @DavidW в порядке, но это не полный ответ на проблему. После небольшого беспокойства я нашел то, что искал: мне нужно было использовать указатели для просмотров памяти, как объяснено в разделе Передача данных из функции C через указатель в разделе документы по Cython. Вот полный рабочий код.

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)
0
ffi23 23 Апр 2019 в 16:33

Первоначально я выявил одну проблему, которая не вызывала вашу проблему, но требовала исправления (теперь это исправлено в отредактированном коде): у Cython нет возможности узнать, что возникло исключение - в C API исключение указывается возвращением 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.

Фактическая проблема - строка my_func(a, c). Преобразование из массива Numpy в представление памяти требует какой-то блокировки (т. Е. GIL) или существует условие гонки с подсчетом ссылок. Это состояние гонки приводит к тому, что он освобождается, когда не должен, отсюда ошибка

Решение состоит в том, чтобы создать представление памяти a вне цикла:

cdef int[:] a_mview = a
# then inside the prange loop
     my_func(a_mview, c).

В рамках параллельной секции хорошо подходит просмотр памяти, но проблема заключается только в первоначальном создании. Я думаю, что Cython не помечает это как ошибку, когда вы компилируете, является ошибкой и может стоить отчетов .

1
DavidW 14 Апр 2019 в 15:38