Прототипирование с использованием кода Python перед компиляцией - PullRequest
20 голосов
/ 19 августа 2008

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

IIRC, один из оригинальных ремитов Python был в качестве языка прототипирования, однако Python довольно либерален в разрешении передачи функций, функторов, объектов в функции и методы, в то время как я подозреваю, что это не так, например, для C или Fortran.

Что я должен знать о разработке функций / классов, которые, как я предполагаю, должны будут взаимодействовать с компилируемым языком? И сколько из этих потенциальных проблем решают такие библиотеки, как cTypes, bgen, SWIG , Boost.Python , Cython или Python SIP * * 1013

Для этого конкретного случая использования (библиотека подбора) я представляю себе, что пользователи могут определять математические функции (Guassian, Lorentzian и т. Д.) Как функции Python, которые затем могут быть интерпретированы библиотекой подбора скомпилированного кода. Передача и возврат массивов также важны.

Ответы [ 7 ]

35 голосов
/ 02 ноября 2009

Наконец-то вопрос, на который я действительно могу дать положительный ответ :).

Я исследовал f2py, boost.python, swig, cython и pyrex для моей работы (доктор философии по методам оптических измерений). Я широко использовал swig, boost.python, pyrex и cython. Я также использовал ctypes. Это моя разбивка:

Отказ от ответственности : Это мой личный опыт. Я не связан ни с одним из этих проектов.

SWIG: не очень хорошо играет с с ++. Должно быть, но проблемы с именами на этапе компоновки стали для меня большой головной болью в Linux и Mac OS X. Если у вас есть код C и вы хотите, чтобы он был связан с python, это хорошее решение. Я упаковал GTS для своих нужд и мне нужно было написать в основном общую библиотеку C, к которой я мог бы подключиться. Я бы не рекомендовал это.

Ctypes: Я написал оболочку libdc1394 (библиотека IEEE Camera), используя ctypes, и это был очень простой опыт. Вы можете найти код на https://launchpad.net/pydc1394. Преобразование заголовков в код Python требует много усилий, но тогда все работает надежно. Это хороший способ, если вы хотите подключить внешнюю библиотеку. Ctypes также находится в stdlib Python, поэтому каждый может использовать ваш код прямо сейчас. Это также хороший способ быстро поиграть с новой библиотекой Python. Я могу рекомендовать его для взаимодействия с внешними библиотеками.

Boost.Python : Очень приятно. Если у вас уже есть собственный код C ++, который вы хотите использовать в python, сделайте это. Таким образом, очень легко перевести структуры классов С ++ в структуры классов Python. Я рекомендую это, если у вас есть код C ++, который вам нужен в Python.

Pyrex / Cython: Используйте Cython, а не Pyrex. Период. Cython более продвинутый и более приятный в использовании. В настоящее время я делаю все с Cython, что я делал с SWIG или Ctypes. Это также лучший способ, если у вас есть код Python, который работает слишком медленно. Процесс абсолютно фантастический: вы конвертируете свои модули Python в модули Cython, собираете их и продолжаете профилирование и оптимизацию, как будто это все еще был Python (никаких изменений инструментов не требуется). Затем вы можете применить столько же (или так мало) кода C, смешанного с вашим кодом Python. Это гораздо быстрее, чем переписывать целые части вашего приложения на C; Вы переписываете только внутренний цикл.

Время : ctypes имеет самые высокие издержки вызова (~ 700 нс), за которыми следует boost.python (322 нс), затем непосредственно swig (290 нс). У Cython самая низкая нагрузка на вызов (124 нс) и лучшая обратная связь, на которую он тратит время (поддержка cProfile!). Числа из моего ящика вызывают тривиальную функцию, которая возвращает целое число из интерактивной оболочки; Поэтому накладные расходы на импорт модуля не рассчитаны, а только на вызовы функций. Поэтому проще и эффективнее быстро получить код Python с помощью профилирования и использования Cython.

Резюме : Для вашей проблемы используйте Cython;). Я надеюсь, что это краткое изложение будет полезно для некоторых людей. Я с удовольствием отвечу на любой оставшийся вопрос.


Редактировать : забываю упомянуть: для числовых целей (то есть подключения к NumPy) используйте Cython; у них есть поддержка для этого (потому что они в основном разрабатывают для этого Cython). Так что это должно быть еще один +1 для вашего решения.

10 голосов
/ 26 августа 2008

Я не использовал SWIG или SIP, но считаю, что написание оболочек Python с boost.python очень мощное и относительно простое в использовании.

Мне не совсем ясно, каковы ваши требования для передачи типов между C / C ++ и python, но вы можете легко это сделать, либо выставив тип C ++ на python, либо используя общий boost :: python :: object аргумент вашего C ++ API. Вы также можете зарегистрировать конвертеры для автоматического преобразования типов Python в типы C ++ и наоборот.

Если вы планируете использовать boost.python, учебник будет хорошим началом.

Я реализовал нечто похожее на то, что вам нужно. У меня есть функция C ++, которая принимает функцию python и изображение в качестве аргументов и применяет функцию python к каждому пикселю изображения.

Image* unary(boost::python::object op, Image& im)
{
    Image* out = new Image(im.width(), im.height(), im.channels());
    for(unsigned int i=0; i<im.size(); i++)
    {
        (*out)[i] == extract<float>(op(im[i]));
    }
    return out;
}

В этом случае Image - это объект C ++, доступный для python (изображение с плавающими пикселями), а op - это определенная Python функция (или фактически любой объект python с атрибутом __call__). Затем вы можете использовать эту функцию следующим образом (предполагая, что унарный находится в вызываемом образе, который также содержит Image и функцию загрузки):

import image
im = image.load('somefile.tiff')
double_im = image.unary(lambda x: 2.0*x, im)

Что касается использования массивов с boost, лично я этого не делал, но я знаю, что функциональность для представления массивов в python с использованием boost доступна - этот может быть полезен.

6 голосов
/ 20 августа 2008

Лучший способ спланировать возможный переход к скомпилированному коду - написать части, чувствительные к производительности, в виде модуля простых функций в функциональном стиле (без сохранения состояния и без побочных эффектов), которые принимают и возвращают основные типы данных.

Это обеспечит взаимно-однозначное сопоставление вашего кода-прототипа Python с возможным скомпилированным кодом и позволит вам легко использовать ctypes и избежать целого ряда головных болей.

Для пиковой подгонки вам почти наверняка понадобятся массивы, которые немного усложнят ситуацию, но все еще очень выполнимы для ctypes.

Если вы действительно хотите использовать более сложные структуры данных или изменить переданные аргументы, SWIG или Стандартный интерфейс расширения C для Python позволит вам делать то, что вы хотите, но с немного хлопот.

Для того, что вы делаете, вы также можете проверить NumPy , который может выполнить часть работы, которую вы хотели бы перенести на C, а также предложить некоторую дополнительную помощь в перемещении данных назад и вперед между Python и C .

4 голосов
/ 30 сентября 2008

f2py (часть numpy) - более простая альтернатива SWIG и boost.python для переноса кода C / Fortran, обрабатывающего числа.

1 голос
/ 19 августа 2008

По моему опыту, есть два простых способа вызова кода C из кода Python. Существуют и другие подходы, все они более раздражающие и / или многословные.

Первый и самый простой способ - скомпилировать кучу кода на C в виде отдельной общей библиотеки, а затем вызвать функции из этой библиотеки, используя ctypes. К сожалению, передавать что-либо, кроме базовых типов данных, нетривиально.

Второй самый простой способ - написать модуль Python на C, а затем вызвать функции в этом модуле. Вы можете передавать все, что вы хотите, в эти функции C без необходимости проходить через любые обручи. И эти функции C легко вызывать функции или методы Python, как описано здесь: https://docs.python.org/extending/extending.html#calling-python-functions-from-c

У меня недостаточно опыта работы с SWIG, чтобы предлагать интеллектуальные комментарии. И хотя можно делать такие вещи, как передавать пользовательские объекты Python в функции C через ctypes, или определять новые классы Python в C, это раздражает и многословно, и я рекомендую воспользоваться одним из двух подходов, описанных выше.

0 голосов
/ 16 мая 2009

В дополнение к вышеперечисленным инструментам я могу порекомендовать использовать Pyrex (для создания модулей расширения Python) или Psyco (как JIT-компилятор для Python).

0 голосов
/ 30 сентября 2008

Python довольно либерален в разрешении передачи функций, функторов, объектов в функции и методы, в то время как я подозреваю, что это не так, скажем, в C или Fortran.

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

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

...