Многопоточный цикл Prange выдает ошибку «двойное освобождение или повреждение (fasttop)» - PullRequest
0 голосов
/ 12 апреля 2019

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

Я хочу запустить функцию в цикле prange Cython, как показано в коде ниже.Этот код выдает ошибку « double free или коррупция (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)

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

Ответы [ 2 ]

1 голос
/ 13 апреля 2019

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

0 голосов
/ 23 апреля 2019

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

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)
...