2016-09-08 3 views
3

Я пишу расширение python, которое записывает в массив numpy от C. Во время тестирования я заметил, что некоторые очень большие массивы будут генерировать segfault, когда я попытаюсь получить доступ к некоторым из их элементов. В частности, последняя строка следующего сегмента кода завершается с Segfault:Почему numpy-C api не предупреждает меня о неудачных распределениях?

// Size of buffer we will write to 
    npy_intp buffer_len_alt = BUFFER_LENGTH; 

    // 
    PyArray_Descr * dtype; 
    dtype = PyArray_DescrFromType(NPY_BYTE); 
    PyObject* column = PyArray_Zeros(1, &buffer_len_alt, dtype, 0); 

    //Check that array creation succeeds 
    if (column == NULL){ 
     // This exit point is not reached, so it looks like everything is OK 
     return (PyObject *) NULL; 
    } 

    // Get the array's internal buffer so we can write to it 
    output_buffer = PyArray_BYTES((PyArrayObject *)column); 

    // Try writing to the buffer 
    output_buffer[0] = 'x'; //No segfault 
    output_buffer[((int) buffer_len_alt) - 1] = 'x'; //Segfault here 

Я проверил и обнаружил, что ошибка возникает только при попытке выделить массив около 3GB (т.е. BUFFER_LENGTH составляет около 3 * 2^30). Это not surprising, что распределение этого размера завершится неудачно, даже если python использует свой собственный распределитель. Что действительно меня беспокоит, так это то, что numpy не вызвало ошибку или иным образом указывает на то, что создание массива не пошло так, как планировалось..

Я уже пробовал проверять PyArray_ISCONTIGUOUS на возвращенном массиве и использовать PyArray_GETCONTIGUOUS, чтобы гарантировать, что это один сегмент памяти, но SEGFAULT все равно произойдет. NPY_ARRAY_DEFAULT создает непрерывные массивы, поэтому в любом случае это не обязательно.

Есть ли какой-то флаг ошибки, который я должен проверять? Как я могу обнаружить/предотвратить эту ситуацию в будущем? Очевидно, что настройка BUFFER_LENGTH на меньшее значение работает, но это значение определяется во время выполнения, и я хотел бы знать точные границы.

РЕДАКТИРОВАТЬ:

Как @DavidW указывалось, ошибка происходит от отливки buffer_len_alt к Int, так как npy_intp может быть 64-битное число. Замена броска на int с приведением в «unsigned long» устраняет проблему для меня

+2

Связано ли это с приведением в int ('((int) buffer_len_alt) - 1'), который, вероятно, подписан, просто не удерживает' BUFFER_LENGTH'? (предполагая 32-битные ints) – DavidW

+0

@DavidW Вы должны сделать ответ. 'buffer_len_alt' имеет тип' npy_intp', который на большинстве платформ представляет собой 64-разрядное целое число со знаком. Если тип 'int' на платформе 32 бит, то' (int) buffer_len_alt' будет отрицательным целым числом, когда 'buffer_len_alt' равно' 3 * (2 ** 30) '. –

+0

@ DavidW Спасибо за совет! Я смог подтвердить, что это действительно проблема. Если вы опубликуете ответ, я обязательно отмечу его как принятый. – Max

ответ

2

Проблема (диагностированная в комментариях) была фактически с поиском массива, а не с распределением массива. Ваш код содержал строку

output_buffer[((int) buffer_len_alt) - 1] = 'x' 

Когда buffer_len_alt (значение приблизительно 3000000000) был приведен к (32 бит) Int (максимальное значение 2147483647), вы в конечном итоге с неправильным адресом, вероятно, большим отрицательным числом.

Решение просто использовать

output_buffer[buffer_len_alt - 1] = 'x' 

(т.е. я не понимаю, почему вы должны нуждаться бросание на всех).

Смежные вопросы