связка Python + C/C++

Вопросы написания собственного программного кода (на любых языках)

Модератор: Olej

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: связка Python + C/C++

Непрочитанное сообщение Olej » 14 авг 2013, 18:30

Olej писал(а): Distutils является фактически стандартным менеджером пакетной системы Pthyton, так же как yum или apt являются менеджерами пакетных систем Fedora или Debian (кроме Distutils есть в Python ещё несколько, 3-4 пакетов того же назначения, но они являются последующим развитием Distutils).
Сами возможности дистрибуции пакетов Python средствами Distutils меня в контексте текущего рассмотрения не интересуют. Но почитать об этом можно и полезно вот в этом, например, свежем переводе - 14.3.1. Краткое описание и дефекты архитектуры Distutils:
Глава 14 из 1 тома книги "Архитектура приложений с открытым исходным кодом".
Оригинал: Python Packaging
Автор: Tarek Ziade
Дата публикации: 7 Июня 2012 г.
Перевод: А.Панин
Дата публикации перевода: 3 апреля 2013 г.
Тем более, что хотя бы минимальное представление о работе Distutils для построения модулей Python из C-кода нужно иметь - простым рассмотрением примеров, как для SWIG, здесь не пробьётесь ;-)

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: связка Python + C/C++

Непрочитанное сообщение Olej » 16 авг 2013, 01:43

Olej писал(а): - использование библиотеки Boost.Python в составе Boost для взаимных коммуникаций C++ с Python - доступна достаточная документация в составе Boost, и описания с примерами с точки зрения Python.
Немного нарушая порядок перечисленных альтернатив - Boost.
Boost.Python рекомендуют на обсуждениях использовать для создания модулей Python написанных на C++.
Немного смущает то, что основная масса материалов на сайте проекта Boost.Python датированы 2002-2005 годами ... но есть отдельные страницы и от 2011-го. Но, может, они всё уже давно написали... ;-)

Boost.Python представляет собой оболочку для написания обёрток-враперов для C++ кода модулей. Для сравнения (и для начала) абсолютно тот же пример модуля, что и для других техник исполнения:

- файл реализации C++ кода - ownmod.cc :

Код: Выделить всё

#include <iostream>
#include <iomanip>
using namespace std;

void echo( void ) {
   cout << "вывод из экспортированного кода!" << endl;
}
- файл обёртки ownmod_wrap.cc - здесь мы его пишем сами, никакой генерации:

Код: Выделить всё

#include <boost/python.hpp>
#include "ownmod.h"

BOOST_PYTHON_MODULE( ownmod ) {
    using namespace boost::python;
    def( "echo", echo );
}
Сборка:

Код: Выделить всё

bash-4.2$ c++ -c -fpic ownmod.cc
bash-4.2$ c++ -c -fpic ownmod_wrap.cc `python-config --includes`
bash-4.2$ c++ -shared ownmod_wrap.o ownmod.o -l boost_python -o ownmod.so
Или так (что то же самое), если не понятен предыдущий вариант:

Код: Выделить всё

bash-4.2$ c++ -c -fpic ownmod.cc
bash-4.2$ c++ -c -fpic ownmod_wrap.cc -I/usr/include/python2.7
bash-4.2$ c++ -shared ownmod_wrap.o ownmod.o -l boost_python -o ownmod.so
Тестовое приложение Python в точности то же, что и в предыдущих технологиях сборки:

Код: Выделить всё

#!/usr/bin/python
# -*- coding: utf-8 -*-

import ownmod
ownmod.echo()
И проверка теста выпонением:

Код: Выделить всё

bash-4.2$ python test.py
вывод из экспортированного кода!
Вложения
first.tgz
(863 байт) 359 скачиваний

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: связка Python + C/C++

Непрочитанное сообщение Olej » 16 авг 2013, 01:55

Olej писал(а):Для сравнения (и для начала) абсолютно тот же пример модуля
В дополнение - ещё один пример, тот который приводится на странице относительно Boost.Python, но в исходном виде он (у меня) не работает (различие версий, ОС, ... что ещё?).
В прилагаемом виде, с минимальными правками, он работает:

Код: Выделить всё

bash-4.2$ make test
python test.py
hello, world
11 * 11 = 121
И, в дополнение к предыдущему примеру, они вместе хорошо показывают логику того как собираются модули используя Boost.Python.

Такой способ изготовления модулей (используя Boost.Python), пожалуй, самый простой в использовании, в сравнении с другими. Хотя и с самыми существенными ограничениями: код модуля пишется только на C++ (как и всё, для чего предназначается Boost), модули изготавливаются только под Python (в отличие от SWIG).

Этот способ может оказаться хорош для быстрого изготовления интерфейсов из Python к уже существующим целевым проектам (библиотекам) C++, например из цифровой обработки сигналов, вэйвлет-разложений, матричных операций и т.п. Причём, даже для тех случаев, когда и код таких целевых С++ библиотек отсутствует, а есть в наличии только сами бинарные библиотеки.
Вложения
libra.tgz
(1.71 КБ) 402 скачивания

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: связка Python + C/C++

Непрочитанное сообщение Olej » 16 авг 2013, 02:02

Я вижу, что примерчики из этой темы довольно активно скачиваются:
ВЛОЖЕНИЯ
first.tgz
(5.96 Кб) Скачиваний: 6
К тем, кого это заинтересовало, убедительная просьба - напишите в 2 слова:
- работает ли в вашем окружении пример?
- в какой ОС (Windows, Linux, Solaris, ...)?
- какая версия Python?
- какой компилятор C/C++ (MS VC, gcc, Clang, ...)?
- есть ли какие артефакты в поведении ... например в выводе русскоязычных сообщений? (которые для того специально и делались русскоязычными)

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: связка Python + C/C++

Непрочитанное сообщение Olej » 16 авг 2013, 14:12

Olej писал(а): - использование проекта Cython ... возможность использования C++, некоторое поверхностное обсуждение см. здесь: Как подключить модуль на C при помощи cython?
И последняя альтернативная техника - Cython ... - последняя из названных 4-х вначале, а не вообще существующих...

Вообще то, Cython - не совсем альтернатива, это своеобразный "клон" языка Python вообще, с совместимым синтаксисом, но позволяющий, с наложением некоторых ограничений на статические определения типов, компилировать ту же программу (в отличие от интерпретации байт-кода Python), что позволяет ускорить выполнение, по некоторым данным, до 100-1000 раз! - Cython:
Cython — язык программирования, упрощающий написание модулей С/С++ кода для Python. Кроме стандартного синтаксиса Python поддерживается:
Прямой вызов функций и методов С/С++ из кода на Cython
Строгая типизация переменных, классов, атрибутов классов
Код Cython преобразуется в С/С++ код для последующей компиляции и впоследствии может использоваться как расширение стандартного Python или как независимое приложение со встроенной библиотекой выполнения Cython.
...
В связи с пониженными накладными расходами в управляющих структурах (особенно в циклах), оптимистической оптимизацией и (ограниченным) выводом типов, скомпилированный Cython код Python обычно выполняется быстрее, чем в CPython 2.6.x, хотя абсолютное улучшение в значительной степени зависит от кода. С объявлениями типов типичные ускорения для численных вычислений/массивов составляет около 100—1000 раз. Для сравнения, типичное увеличение скорости при использовании Psyco (JIT-компилятор для Python) составляет около 4—100 раз.
Раз производится компиляция Cython, да ещё и в C/C++, то увязывание на этом этапе собственного кода модулей C/C++ - дело для Cython естественное и несложное.

Сайт проекта, там же обширная документация.
Есть русскоязычный перевод документации, весьма полного объёма... (для беглого ознакомления вполне удобно, хотя перевод по версии 0.15, а оригинальная документация по 0.19, так что позже нужно пользоваться только ней).
Там же, на сайте, есть инсталляции, но пакет присутствует в пакетных репозитариях практически всех дистрибутивов Linux:

Код: Выделить всё

bash-4.2$ yum list cython
...
0 packages excluded due to repository protections
Доступные пакеты
Cython.i686                                               0.18-1.fc17                                                updates

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: связка Python + C/C++

Непрочитанное сообщение Olej » 16 авг 2013, 15:16

Olej писал(а): Раз производится компиляция Cython, да ещё и в C/C++, то увязывание на этом этапе собственного кода модулей C/C++ - дело для Cython естественное и несложное.
1. Чаще всего, для создания Python-модулей на C/C++ используют Cython совместно с Distutils (как обсуждалось выше), и именно такой способ массово мелькает в обсуждениях. Но это не единственный способ ... и может и не всегда лучший.

2. Интересно само по себе посмотреть (мне, по крайней мере интересно) как вообще Cython компилирует Python-код, даже безотносительно к реализации на C/C++. Потому, что это нужно, как минимум, для разбирательств с тем, как туда затолкать ещё и файл на C/C++ ... а во-вторых, это просто очень интересно - как от интерпретации в Python переходят к выполнению точно того же кода, но компилированного в Cython.

- файл модуля того же смысла, что и в предыдущих проектах first.tgz, только теперь файл модуля имеет имя ownmod.pyx (.pyx но не .py!):

Код: Выделить всё

def echo():
   print "вывод из компилированного (cython) кода!"
- тестовая ... "программа" ;-) (файл test.py):

Код: Выделить всё

#!/usr/bin/python
# -*- coding: utf-8 -*-

import ownmod
ownmod.echo()
- файл setup.py (для совместного использования с disputils):

Код: Выделить всё

from distutils.core import setup
from distutils.extension import Extension
from Cython.Distutils import build_ext

ext_modules = [ Extension( "ownmod", [ "ownmod.pyx"] ) ]

setup(
  name = 'first app',
  cmdclass = { 'build_ext': build_ext },
  ext_modules = ext_modules
)
Всё! :

Код: Выделить всё

bash-4.2$ python setup.py build_ext --inplace
running build_ext
cythoning ownmod.pyx to ownmod.c
building 'ownmod' extension
creating build
creating build/temp.linux-i686-2.7
gcc -pthread -fno-strict-aliasing -O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector --param=ssp-buffer-size=4 -m32 -march=i686 -mtune=atom -fasynchronous-unwind-tables -D_GNU_SOURCE -fPIC -fwrapv -DNDEBUG -O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector --param=ssp-buffer-size=4 -m32 -march=i686 -mtune=atom -fasynchronous-unwind-tables -D_GNU_SOURCE -fPIC -fwrapv -fPIC -I/usr/include/python2.7 -c ownmod.c -o build/temp.linux-i686-2.7/ownmod.o
gcc -pthread -shared -Wl,-z,relro build/temp.linux-i686-2.7/ownmod.o -L/usr/lib -lpython2.7 -o /home/olej/2013_WORK/Python/CPython/cython/pfirst/ownmod.so
Вот он модуль, только теперь он не ownmod.py (на Python), а ownmod.so (бинарный компилированный код, но полученный из кода на Python! - это и есть то, что может быть до 1000 раз быстрее, чем просто выполнение в Python):

Код: Выделить всё

bash-4.2$ ls *.so
ownmod.so
И проверка:

Код: Выделить всё

bash-4.2$ python test.py
вывод из компилированного (cython) кода!
P.S. Архив назван всё-таки pfirst.tgz, а не first.tgz, как в предыдущих вариантах, поскольку код модуля ещё (пока) написан на Python.
Вложения
pfirst.tgz
(2.33 КБ) 416 скачиваний

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: связка Python + C/C++

Непрочитанное сообщение Olej » 18 авг 2013, 22:48

Olej писал(а): Вообще то, Cython - не совсем альтернатива, это своеобразный "клон" языка Python вообще, с совместимым синтаксисом, но позволяющий, с наложением некоторых ограничений на статические определения типов, компилировать ту же программу (в отличие от интерпретации байт-кода Python), что позволяет ускорить выполнение, по некоторым данным, до 100-1000 раз!
Мне кажется, что вот это такой интересный пример:
- 3 эквивалентные по функциональности реализации - длинный цикл тупых математических вычислений, в данном случае с функцией sin(x), но это не важно...
- а). чисто на Python вариант и выполняемый в классическом интерпретаторе CPython, б). то же, но компилировано Cython, в). та же компиляция Cython, но вместо вызова math.sin( x ) из Python модуля math, вызывается C-реализация sin( x ) из стандартной C-библиотеки libm.so.
- ... и контролируем время выполнения...

- файл psin.py, реализующий вариант а). :

Код: Выделить всё

import time
import math

def sin0( n ):
    x = 0.0
    clc = time.time()
    for i in xrange( n ):
        x += math.sin( x )
    return time.time() - clc


- файл csin.pyx, реализующий варианты б). и в). :

Код: Выделить всё

import time
import math

def sin1( int n ):
    cdef int i
    cdef double x, clc
    x = 0.0
    clc = time.time()
    for i in xrange( n ):
        x += math.sin( x )
    return time.time() - clc

cdef extern from "math.h":
    double sin( double )

def sin2( n ):
    x = 0.0
    clc = time.time()
    for i in xrange( n ):
        x += sin( x )
    return time.time() - clc
- файл setup.py для сборки модуля csin.so (обратите внимание как подключена стандартная библиотека libm.so) :

Код: Выделить всё

from distutils.core import setup
from distutils.extension import Extension
from Cython.Distutils import build_ext

ext_modules = [ Extension( "csin", [ "csin.pyx" ], libraries=["m"] ) ]

setup(
  cmdclass = { 'build_ext': build_ext },
  ext_modules = ext_modules
)
- файл сборки Makefile (можно и вручную собрать той же командой):

Код: Выделить всё

all *.so:    setup.py
        python setup.py build_ext --inplace

clean:
        rm -f *.c *.so *.pyc *.pyo
        rm -f -R build

test:   *.so
        python test.py
- и тестовый файл test.py, которым это всё проверяется:

Код: Выделить всё

#!/usr/bin/python
# -*- coding: utf-8 -*-

from sys import argv
from psin import sin0 as t1
from csin import sin1 as t2
from csin import sin2 as t3
#from csin import sin3 as t4

n = ( len( argv ) > 1 and int( argv[ 1 ] ) ) or 5000000

print "время %i вычислений %5.2f секунд" % ( n, t1( n ) )
print "время %i вычислений %5.2f секунд" % ( n, t2( n ) )
print "время %i вычислений %5.2f секунд" % ( n, t3( n ) )
А теперь выполнение ... достаточно неожиданное:

Код: Выделить всё

bash-4.2$ ./test.py 10000000
время 10000000 вычислений  4.81 секунд
время 10000000 вычислений  5.33 секунд
время 10000000 вычислений  0.29 секунд
- время компилированного выполнения цикла даже больше простой интерпретации в CPython, хотя переменные цикла и типизированы - http://kostikvento.ru/cython/docs/src/quickstart/cythonize.html:
Поскольку для переменной цикла i явно указан тип из семантики C, цикл for будет непосредственно преобразован в чистый код на C. Объявление типов для переменных a, s и dx существенно, поскольку они участвуют в вычислениях внутри цикла; типирование b и N не дает особого вклада, зато мы будем последовательны и применим типирование переменных во всем коде функции (в данном случае для этого не придется много работать).

Это приводит уже к 4-кратному ускорению по сравнению с версией на чистом Питоне.
- это связано, полагаю, с существенным временем самого вычисления sin( x ) в штатном модуле math, намного больше самого времени операций в цикле...

- но вот подключенный для тех же целей код C выполняется в 20 раз быстрее.

P.S. не ускорение операций в компилируемом коде нужно списать, думаю, ещё и на постоянно улучшающуюся от версии к версии производительность интерпретирующей системы байт-кода CPython.
Вложения
math.tgz
(1.28 КБ) 395 скачиваний

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: связка Python + C/C++

Непрочитанное сообщение Olej » 18 авг 2013, 23:39

Olej писал(а): 1. Чаще всего, для создания Python-модулей на C/C++ используют Cython совместно с Distutils (как обсуждалось выше), и именно такой способ массово мелькает в обсуждениях. Но это не единственный способ ... и может и не всегда лучший.
И возвращаясь к тому, с чего начали, и из-за чего весь разговор: как с Cython подключить свой C-код как модуль в Pthon?

Ниже - простейший пример (потому как только простейшие примеры показывают ясно).
Причём я совместил 2 варианта в 1 архив:
а). C-функция (cecho()) описана в .pyx файле (online) и будет компилироваться в C-код с помощью GCC компилятора;
б). реализация C-функции (cecho()) вынесена в отдельный C-файл, который компилируется раздельно, и потом уже линкуется в DLL библиотеку (.so) собираемого модуля (это тот окончательный случай, который можно легко расширить на C-код любой степени сложности!)

Вариант а).:
- файл ownmod1.pyx:

Код: Выделить всё

cdef void cecho() :
   print "вывод из компилированного (cython) кода!"

def echo():
    cecho()

- соответствующий файл setup1.py для сборки модуля:

Код: Выделить всё

from distutils.core import setup
from distutils.extension import Extension
from Cython.Distutils import build_ext

ext_modules = [ Extension( "ownmod1", [ "ownmod1.pyx" ] ) ]

setup(
  cmdclass = { 'build_ext': build_ext },
  ext_modules = ext_modules
)
Вариант б)., полученный из предыдущего варианта а).:
- файл ownmod2.pyx:

Код: Выделить всё

cdef extern void cecho()

def echo():
    cecho()
- файл echo.c, куда отдельно вынесена реализация функции echo():

Код: Выделить всё

#include <stdio.h>

void cecho( void ) {
   printf( "вывод из импортированного C-кода!\n" );
}
- соответствующий файл setup1.py для сборки модуля (обратите внимание как в списке файлов модуля смешались файлы разного типа: Cython/Python код .pyx + C-код .c):

Код: Выделить всё

from distutils.core import setup
from distutils.extension import Extension
from Cython.Distutils import build_ext

ext_modules = [ Extension( "ownmod2", [ "ownmod2.pyx", "echo.c"] ) ]

setup(
  cmdclass = { 'build_ext': build_ext },
  ext_modules = ext_modules
)
Тест полученных модулей ./test.py :

Код: Выделить всё

#!/usr/bin/python
# -*- coding: utf-8 -*-

import ownmod1
import ownmod2

ownmod1.echo()
ownmod2.echo()

Код: Выделить всё

bash-4.2$ python ./test.py 
вывод из компилированного (cython) кода!
вывод из импортированного C-кода!
Вложения
xfirst.tgz
(1.25 КБ) 389 скачиваний

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: связка Python + C/C++

Непрочитанное сообщение Olej » 18 авг 2013, 23:43

Olej писал(а): Как минимум из широко используемых и обсуждаемых это:

- использование пакета swig (это ссылка на его документацию)... этот пакет есть в репозитарии практически каждого дистрибутива Linux, так что его и собирать его не надо...

- использование пакета Python distutils, хороший и достаточный пример обсуждается здесь: Python + C API simple example memory leak

- использование проекта Cython ... возможность использования C++, некоторое поверхностное обсуждение см. здесь: Как подключить модуль на C при помощи cython?

- использование библиотеки Boost.Python в составе Boost для взаимных коммуникаций C++ с Python - доступна достаточная документация в составе Boost, и описания с примерами с точки зрения Python.

Интересно проделать параллельные примеры с использованием разных инструментов и сравнить их меж собой...
Вот теперь - всё ;-)
По этим 4-м вариантам изготовления расширений к Python сделаны примеры, как мне кажется, более чем достаточные, чтобы, используя их в качестве начальных приближений, делать модули в C-коде любой сложности.

Если я буду видеть (или мне подскажут) ещё другие варианты для этого дела - я и по ним сделаю подобные примеры...

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: связка Python + C/C++

Непрочитанное сообщение Olej » 19 авг 2013, 14:36

Olej писал(а):По этим 4-м вариантам изготовления расширений к Python сделаны примеры, как мне кажется, более чем достаточные, чтобы, используя их в качестве начальных приближений, делать модули в C-коде любой сложности.

Если я буду видеть (или мне подскажут) ещё другие варианты для этого дела - я и по ним сделаю подобные примеры...
5-м вариантом есть использование модуля ctypes из стандартной библиотеки модулей Python (/usr/lib/python2.7).
Пример использования взят мной отсюда и немного изменён:

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

- файл myfib.c - C-реализация:

Код: Выделить всё

unsigned long fib( unsigned long x ) {
    if( x < 2 ) return x;
    return fib( x - 1 ) + fib( x - 2 );
}


- сборку модуля (DLL) делаем (у меня это в Makefile: make):

Код: Выделить всё

bash-4.2$ gcc -shared -Wl,-soname,myfib.so -o myfib.so -fPIC myfib.c
- тестовый файл test.py, содержащий абсолютно аналогичную реализацию на Python + 2 вызова для сравнения скорости (времени) выполнения:

Код: Выделить всё

#!/usr/bin/python
# -*- coding: utf-8 -*-

import sys
import ctypes
import timeit

n = ( len( sys.argv ) > 1 and int( sys.argv[ 1 ] ) ) or 32

fib = ctypes.CDLL( './myfib.so' ).fib
fib.restype = ctypes.c_long
fib.argtypes = ( ctypes.c_ulong, )

print timeit.timeit( 'fib( %s )' % n, 'from __main__ import fib', number = 1 )

def pyfib( x ):
    if x < 2: return x
    return pyfib( x - 1 ) + pyfib( x - 2 )

print timeit.timeit( 'pyfib( %s )' % n, 'from __main__ import pyfib', number = 1 )
Результаты:

Код: Выделить всё

bash-4.2$ python ./test.py
0.102193117142
2.89224910736
bash-4.2$ python ./test.py 40
4.81338000298
134.935367107
Вот так: C-реализация в 30 и больше раз быстрее!

P.S. Скорость роста времени выполнения от N такой реализации - экспоненциальная. Но запуск вызовов ctypes происходит при захваченной блокировке GIT (пресловутой ;-) ), поэтому остановить выполнение по Ctrl+C - не получится:

Код: Выделить всё

bash-4.2$ python ./test.py 50
^C^C^C
Завершено
А завершить это удаётся только по выполнению в другом терминале:

Код: Выделить всё

bash-4.2$ killall python
Вложения
fibo.tgz
(867 байт) 391 скачивание

Ответить

Вернуться в «Программирование»

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и 17 гостей