Галид с макетами C NumPy массивов
Я начинаю использовать Halide и использовать его из среды Python. В этой среде Python данные передаются в виде массивов Numpy, которые фактически являются псевдонимами для массива C++, определенного в другом месте.
Однако, когда я использую функцию Halide, я получаю ошибку:
Нарушено ограничение: img.stride.0 (520) == 1 (1) Прервано (ядро сброшено)
которая может быть "решена" путем копирования пустых массивов в массивы компоновки Fortran:
img=np.copy(img,order="F")
res=np.copy(res,order="F")
с img и res мои входные и выходные изображения. Однако обратите внимание, что это включает в себя дополнительные операции копирования, что очень плохо для общего доступа к глобальной памяти.
Как я могу обойти эту проблему? Я подумал о том, чтобы на самом деле сказать Python, что мои массивы имеют разметку Fortran и правильно переключены индексы.... Однако в настоящее время я использую PyArray_SimpleNewFromData для получения массивов Python (без фактического копирования данных), и это приводит к Массивы в стиле C.
2 ответа
Проблема в том, что PyArray_SimpleNewFromData
сделал из данных ndarray в стиле C, где в коде хоста C++ массивы выполнены в стиле Fortran. Решение состоит в том, чтобы преобразовать ndarrays сразу после их создания, что можно сделать с помощью следующего кода:
def swap(img):
(sh1,sh2)=img.shape
(st1,st2)=img.strides
img.shape=(sh2,sh1)
img.strides=(st2,st1)
После этого в Halide мы обычно можем векторизовать в нулевой (x) размерности.
Изначально Halide ожидает хранения на уровне строки, но индексирует такие вещи: im(col, row)... и это выглядит очень похоже на хранилище на уровне столбца для того, кто привык обрабатывать изображения как матрицы или использовать двумерные массивы в C.
Таким образом, вы можете изменить индексирование в соответствии с понятием Halide или сказать Halide, что ваш макет памяти работает наоборот (шаг (0) велик).
Здесь есть учебник, который охватывает тесно связанную тему: http://halide-lang.org/tutorials/tutorial_lesson_16_rgb_generate.html
Краткая версия для 2D входов и функций:
image_param.set_stride(0, Expr()).set_stride(1, 1);
output_func.output_buffer().set_stride(0, Expr()).set_stride(1, 1);
Первый вызов set_stride освобождает шаг в измерении 0, а второй сообщает Halide, что он может предполагать, что шаг в измерении 1 равен 1. Если вы сделаете это, вы захотите векторизовать ваши функции Halide во втором измерении, потому что это тот это плотно в памяти:
f(i, j) = ...
f.vectorize(j, 4)