Extendiendo python – para tomar un gulp, no para un swig o Cython

Encontré el cuello de botella en mi código de Python, jugué un poco con psycho, etc. Luego decidí escribir la extensión ac / c ++ para el rendimiento.

Con la ayuda de Swig casi no necesita preocuparse por los argumentos, etc. Todo funciona bien.

Ahora mi pregunta: swig crea un archivo py bastante grande que hace muchas “verificaciones” y “PySwigObject” antes de llamar al código real .pyd o .so.

¿Alguno de ustedes tiene alguna experiencia sobre si hay más rendimiento que ganar si escribe este archivo a mano o si deja que Swig lo haga?

Seguro que siempre tendrás una ganancia de rendimiento al hacer esto a mano, pero la ganancia será muy pequeña en comparación con el esfuerzo requerido para hacerlo. No tengo ninguna figura que darle, pero no lo recomiendo, porque necesitará mantener la interfaz a mano, ¡y esta no es una opción si su módulo es grande!

Hizo lo correcto al elegir usar un lenguaje de scripting porque quería un desarrollo rápido. De esta manera, ha evitado el síndrome de optimización temprana y ahora desea optimizar las partes del cuello de botella, ¡excelente! Pero si haces la interfaz C / python a mano, seguramente te caerás en el síndrome de optimización temprana.

Si desea algo con menos código de interfaz, puede pensar en crear una dll a partir de su código C y usar esa biblioteca directamente desde python con cstruct .

Considere también Cython si desea usar solo el código de Python en su progtwig.

Debería considerar Boost.Python si no planea generar enlaces para otros idiomas también con swig.

Si tiene muchas funciones y clases para vincular, Py ++ es una gran herramienta que genera automáticamente el código necesario para realizar los enlaces.

Pybindgen también puede ser una opción, pero es un proyecto nuevo y menos completo que Boost.Python.


Editar:

Tal vez necesito ser más explícito sobre pro y contra.

  • Trago:

    pro: puedes generar enlaces para muchos lenguajes de scripting.

    contras: no me gusta la forma en que funciona el analizador. No sé si hubo algún progreso, pero hace dos años el analizador de C ++ era bastante limitado. La mayoría de las veces tuve que copiar / pasar mis encabezados .h, agregar algunos % caracteres y dar pistas adicionales al analizador de swig.

    También me fue necesario lidiar con Python C-API de vez en cuando para (no tan) conversiones de tipo complicadas.

    Ya no lo estoy usando.

  • Boost.Python:

    pro: Es una biblioteca muy completa. Te permite hacer casi todo lo posible con C-API, pero en C ++. Nunca tuve que escribir código C-API con esta biblioteca. Nunca me encontré con un error debido a la biblioteca. El código para las encuadernaciones funciona como un amuleto o un comstackdo de rechazo

    Probablemente sea una de las mejores soluciones disponibles actualmente si ya tiene alguna biblioteca de C ++ para enlazar. Pero si solo tienes que reescribir una pequeña función C, probablemente lo intentaría con Cython.

    contras: si no tiene una biblioteca Boost.Python precomstackda, va a utilizar Bjam (tipo de reemplazo). Realmente odio Bjam y su syntax.

    Las bibliotecas de Python creadas con BP tienden a volverse obesas. También toma mucho tiempo comstackrlos.

  • Py ++ (descontinuado): es Boost.Python hecho fácil. Py ++ utiliza un analizador de C ++ para leer su código y luego genera el código Boost.Python automáticamente. También tienes un gran apoyo de su autor (no, no soy yo ;-)).

    contras: sólo los problemas debidos a Boost.Python en sí. Actualización: A partir de 2014 este proyecto ahora parece suspendido.

  • Pybindgen:

    Genera el código que se ocupa de la C-API. Puede describir funciones y clases en un archivo de Python, o dejar que Pybindgen lea sus encabezados y genere enlaces automáticamente (para esto utiliza pygccxml, una biblioteca de python escrita por el autor de Py ++).

    contras: es un proyecto joven, con un equipo más pequeño que Boost.Python. Todavía hay algunas limitaciones: no puede usar herencia múltiple para sus clases de C ++, devoluciones de llamada (no automáticamente, aunque se puede escribir un código de manejo de callback personalizado). Traducción de excepciones de Python a C.

    Definitivamente vale la pena un buen vistazo.

  • Una nueva: el 20/01/2009, el autor de Py ++ anunció un nuevo paquete para la conexión de código C / C ++ con python. Se basa en ctypes. ¡No lo intenté ya pero lo haré! Nota: este proyecto se ve descontrolado, como Py ++.

  • CFFI : No sabía la existencia de este hasta hace muy poco, así que por ahora no puedo dar mi opinión. Parece que puede definir funciones C en cadenas de Python y llamarlas directamente desde el mismo módulo de Python.

  • Cython : este es el método que estoy usando actualmente en mis proyectos. Básicamente se escribe código en archivos especiales .pyx. Esos archivos se comstackn (traducen) en código C, que a su vez se comstackn en módulos CPython. El código de Cython puede parecerse a Python normal (y, de hecho, Python puro son archivos .pyx de Cython válidos), pero también puede obtener más información como tipos de variables. Esta escritura opcional permite a Cython generar un código C más rápido. El código en los archivos de Cython puede llamar a ambas funciones puras de Python pero también a las funciones C y C ++ (y también a los métodos C ++).

    Me tomó algo de tiempo pensar en Cython, que en el mismo código llaman a las funciones C y C ++, mezclan las variables Python y C, etc. Pero es un lenguaje muy poderoso, con una comunidad activa (en 2014) y amigable.

SWIG 2.0.4 ha introducido una nueva opción incorporada que mejora el rendimiento. Hice algunas pruebas comparativas utilizando un progtwig de ejemplo que hace muchas llamadas rápidas a una extensión de C ++. Construí la extensión usando boost.python, PyBindGen, SIP y SWIG con y sin la opción -builtin. Aquí están los resultados (promedio de 100 carreras):

 SWIG with -builtin 2.67s SIP 2.70s PyBindGen 2.74s boost.python 3.07s SWIG without -builtin 4.65s 

SWIG solía ser el más lento. Con la nueva opción incorporada, SWIG parece ser el más rápido.

Usar Cython es bastante bueno. Puede escribir su extensión C con una syntax similar a Python y hacer que genere un código C. Placa de caldera incluida. Dado que ya tiene el código en Python, solo tiene que hacer unos pocos cambios en su código de cuello de botella y de ahí se generará el código C.

Ejemplo. hello.pyx :

 cdef int hello(int a, int b): return a + b 

Eso genera 601 líneas de código repetitivo:

 /* Generated by Cython 0.10.3 on Mon Jan 19 08:24:44 2009 */ #define PY_SSIZE_T_CLEAN #include "Python.h" #include "structmember.h" #ifndef PY_LONG_LONG #define PY_LONG_LONG LONG_LONG #endif #ifndef DL_EXPORT #define DL_EXPORT(t) t #endif #if PY_VERSION_HEX < 0x02040000 #define METH_COEXIST 0 #endif #if PY_VERSION_HEX < 0x02050000 typedef int Py_ssize_t; #define PY_SSIZE_T_MAX INT_MAX #define PY_SSIZE_T_MIN INT_MIN #define PyInt_FromSsize_t(z) PyInt_FromLong(z) #define PyInt_AsSsize_t(o) PyInt_AsLong(o) #define PyNumber_Index(o) PyNumber_Int(o) #define PyIndex_Check(o) PyNumber_Check(o) #endif #if PY_VERSION_HEX < 0x02060000 #define Py_REFCNT(ob) (((PyObject*)(ob))->ob_refcnt) #define Py_TYPE(ob) (((PyObject*)(ob))->ob_type) #define Py_SIZE(ob) (((PyVarObject*)(ob))->ob_size) #define PyVarObject_HEAD_INIT(type, size) \ PyObject_HEAD_INIT(type) size, #define PyType_Modified(t) typedef struct { void *buf; PyObject *obj; Py_ssize_t len; Py_ssize_t itemsize; int readonly; int ndim; char *format; Py_ssize_t *shape; Py_ssize_t *strides; Py_ssize_t *suboffsets; void *internal; } Py_buffer; #define PyBUF_SIMPLE 0 #define PyBUF_WRITABLE 0x0001 #define PyBUF_LOCK 0x0002 #define PyBUF_FORMAT 0x0004 #define PyBUF_ND 0x0008 #define PyBUF_STRIDES (0x0010 | PyBUF_ND) #define PyBUF_C_CONTIGUOUS (0x0020 | PyBUF_STRIDES) #define PyBUF_F_CONTIGUOUS (0x0040 | PyBUF_STRIDES) #define PyBUF_ANY_CONTIGUOUS (0x0080 | PyBUF_STRIDES) #define PyBUF_INDIRECT (0x0100 | PyBUF_STRIDES) #endif #if PY_MAJOR_VERSION < 3 #define __Pyx_BUILTIN_MODULE_NAME "__builtin__" #else #define __Pyx_BUILTIN_MODULE_NAME "builtins" #endif #if PY_MAJOR_VERSION >= 3 #define Py_TPFLAGS_CHECKTYPES 0 #define Py_TPFLAGS_HAVE_INDEX 0 #endif #if (PY_VERSION_HEX < 0x02060000) || (PY_MAJOR_VERSION >= 3) #define Py_TPFLAGS_HAVE_NEWBUFFER 0 #endif #if PY_MAJOR_VERSION >= 3 #define PyBaseString_Type PyUnicode_Type #define PyString_Type PyBytes_Type #define PyInt_Type PyLong_Type #define PyInt_Check(op) PyLong_Check(op) #define PyInt_CheckExact(op) PyLong_CheckExact(op) #define PyInt_FromString PyLong_FromString #define PyInt_FromUnicode PyLong_FromUnicode #define PyInt_FromLong PyLong_FromLong #define PyInt_FromSize_t PyLong_FromSize_t #define PyInt_FromSsize_t PyLong_FromSsize_t #define PyInt_AsLong PyLong_AsLong #define PyInt_AS_LONG PyLong_AS_LONG #define PyInt_AsSsize_t PyLong_AsSsize_t #define PyInt_AsUnsignedLongMask PyLong_AsUnsignedLongMask #define PyInt_AsUnsignedLongLongMask PyLong_AsUnsignedLongLongMask #define __Pyx_PyNumber_Divide(x,y) PyNumber_TrueDivide(x,y) #else #define __Pyx_PyNumber_Divide(x,y) PyNumber_Divide(x,y) #define PyBytes_Type PyString_Type #endif #if PY_MAJOR_VERSION >= 3 #define PyMethod_New(func, self, klass) PyInstanceMethod_New(func) #endif #if !defined(WIN32) && !defined(MS_WINDOWS) #ifndef __stdcall #define __stdcall #endif #ifndef __cdecl #define __cdecl #endif #else #define _USE_MATH_DEFINES #endif #ifdef __cplusplus #define __PYX_EXTERN_C extern "C" #else #define __PYX_EXTERN_C extern #endif #include  #define __PYX_HAVE_API__helloworld #ifdef __GNUC__ #define INLINE __inline__ #elif _WIN32 #define INLINE __inline #else #define INLINE #endif typedef struct {PyObject **p; char *s; long n; char is_unicode; char intern; char is_identifier;} __Pyx_StringTabEntry; /*proto*/ static int __pyx_skip_dispatch = 0; /* Type Conversion Predeclarations */ #if PY_MAJOR_VERSION < 3 #define __Pyx_PyBytes_FromString PyString_FromString #define __Pyx_PyBytes_AsString PyString_AsString #else #define __Pyx_PyBytes_FromString PyBytes_FromString #define __Pyx_PyBytes_AsString PyBytes_AsString #endif #define __Pyx_PyBool_FromLong(b) ((b) ? (Py_INCREF(Py_True), Py_True) : (Py_INCREF(Py_False), Py_False)) static INLINE int __Pyx_PyObject_IsTrue(PyObject* x); static INLINE PY_LONG_LONG __pyx_PyInt_AsLongLong(PyObject* x); static INLINE unsigned PY_LONG_LONG __pyx_PyInt_AsUnsignedLongLong(PyObject* x); static INLINE Py_ssize_t __pyx_PyIndex_AsSsize_t(PyObject* b); #define __pyx_PyInt_AsLong(x) (PyInt_CheckExact(x) ? PyInt_AS_LONG(x) : PyInt_AsLong(x)) #define __pyx_PyFloat_AsDouble(x) (PyFloat_CheckExact(x) ? PyFloat_AS_DOUBLE(x) : PyFloat_AsDouble(x)) static INLINE unsigned char __pyx_PyInt_unsigned_char(PyObject* x); static INLINE unsigned short __pyx_PyInt_unsigned_short(PyObject* x); static INLINE char __pyx_PyInt_char(PyObject* x); static INLINE short __pyx_PyInt_short(PyObject* x); static INLINE int __pyx_PyInt_int(PyObject* x); static INLINE long __pyx_PyInt_long(PyObject* x); static INLINE signed char __pyx_PyInt_signed_char(PyObject* x); static INLINE signed short __pyx_PyInt_signed_short(PyObject* x); static INLINE signed int __pyx_PyInt_signed_int(PyObject* x); static INLINE signed long __pyx_PyInt_signed_long(PyObject* x); static INLINE long double __pyx_PyInt_long_double(PyObject* x); #ifdef __GNUC__ /* Test for GCC > 2.95 */ #if __GNUC__ > 2 || (__GNUC__ == 2 && (__GNUC_MINOR__ > 95)) #define likely(x) __builtin_expect(!!(x), 1) #define unlikely(x) __builtin_expect(!!(x), 0) #else /* __GNUC__ > 2 ... */ #define likely(x) (x) #define unlikely(x) (x) #endif /* __GNUC__ > 2 ... */ #else /* __GNUC__ */ #define likely(x) (x) #define unlikely(x) (x) #endif /* __GNUC__ */ static PyObject *__pyx_m; static PyObject *__pyx_b; static PyObject *__pyx_empty_tuple; static int __pyx_lineno; static int __pyx_clineno = 0; static const char * __pyx_cfilenm= __FILE__; static const char *__pyx_filename; static const char **__pyx_f; static void __Pyx_AddTraceback(const char *funcname); /*proto*/ /* Type declarations */ /* Module declarations from helloworld */ static int __pyx_f_10helloworld_hello(int, int); /*proto*/ /* Implementation of helloworld */ /* "/home/nosklo/devel/ctest/hello.pyx":1 * cdef int hello(int a, int b): # <<<<<<<<<<<<<< * return a + b * */ static int __pyx_f_10helloworld_hello(int __pyx_v_a, int __pyx_v_b) { int __pyx_r; /* "/home/nosklo/devel/ctest/hello.pyx":2 * cdef int hello(int a, int b): * return a + b # <<<<<<<<<<<<<< * */ __pyx_r = (__pyx_v_a + __pyx_v_b); goto __pyx_L0; __pyx_r = 0; __pyx_L0:; return __pyx_r; } static struct PyMethodDef __pyx_methods[] = { {0, 0, 0, 0} }; static void __pyx_init_filenames(void); /*proto*/ #if PY_MAJOR_VERSION >= 3 static struct PyModuleDef __pyx_moduledef = { PyModuleDef_HEAD_INIT, "helloworld", 0, /* m_doc */ -1, /* m_size */ __pyx_methods /* m_methods */, NULL, /* m_reload */ NULL, /* m_traverse */ NULL, /* m_clear */ NULL /* m_free */ }; #endif static int __Pyx_InitCachedBuiltins(void) { return 0; return -1; } static int __Pyx_InitGlobals(void) { return 0; return -1; } #if PY_MAJOR_VERSION < 3 PyMODINIT_FUNC inithelloworld(void); /*proto*/ PyMODINIT_FUNC inithelloworld(void) #else PyMODINIT_FUNC PyInit_helloworld(void); /*proto*/ PyMODINIT_FUNC PyInit_helloworld(void) #endif { __pyx_empty_tuple = PyTuple_New(0); if (unlikely(!__pyx_empty_tuple)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 1; __pyx_clineno = __LINE__; goto __pyx_L1_error;} /*--- Library function declarations ---*/ __pyx_init_filenames(); /*--- Initialize various global constants etc. ---*/ if (unlikely(__Pyx_InitGlobals() < 0)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 1; __pyx_clineno = __LINE__; goto __pyx_L1_error;} /*--- Module creation code ---*/ #if PY_MAJOR_VERSION < 3 __pyx_m = Py_InitModule4("helloworld", __pyx_methods, 0, 0, PYTHON_API_VERSION); #else __pyx_m = PyModule_Create(&__pyx_moduledef); #endif if (!__pyx_m) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 1; __pyx_clineno = __LINE__; goto __pyx_L1_error;}; #if PY_MAJOR_VERSION < 3 Py_INCREF(__pyx_m); #endif __pyx_b = PyImport_AddModule(__Pyx_BUILTIN_MODULE_NAME); if (!__pyx_b) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 1; __pyx_clineno = __LINE__; goto __pyx_L1_error;}; if (PyObject_SetAttrString(__pyx_m, "__builtins__", __pyx_b) < 0) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 1; __pyx_clineno = __LINE__; goto __pyx_L1_error;}; /*--- Builtin init code ---*/ if (unlikely(__Pyx_InitCachedBuiltins() < 0)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 1; __pyx_clineno = __LINE__; goto __pyx_L1_error;} __pyx_skip_dispatch = 0; /*--- Global init code ---*/ /*--- Function export code ---*/ /*--- Type init code ---*/ /*--- Type import code ---*/ /*--- Function import code ---*/ /*--- Execution code ---*/ /* "/home/nosklo/devel/ctest/hello.pyx":1 * cdef int hello(int a, int b): # <<<<<<<<<<<<<< * return a + b * */ #if PY_MAJOR_VERSION < 3 return; #else return __pyx_m; #endif __pyx_L1_error:; __Pyx_AddTraceback("helloworld"); #if PY_MAJOR_VERSION >= 3 return NULL; #endif } static const char *__pyx_filenames[] = { "hello.pyx", }; /* Runtime support code */ static void __pyx_init_filenames(void) { __pyx_f = __pyx_filenames; } #include "compile.h" #include "frameobject.h" #include "traceback.h" static void __Pyx_AddTraceback(const char *funcname) { PyObject *py_srcfile = 0; PyObject *py_funcname = 0; PyObject *py_globals = 0; PyObject *empty_string = 0; PyCodeObject *py_code = 0; PyFrameObject *py_frame = 0; #if PY_MAJOR_VERSION < 3 py_srcfile = PyString_FromString(__pyx_filename); #else py_srcfile = PyUnicode_FromString(__pyx_filename); #endif if (!py_srcfile) goto bad; if (__pyx_clineno) { #if PY_MAJOR_VERSION < 3 py_funcname = PyString_FromFormat( "%s (%s:%d)", funcname, __pyx_cfilenm, __pyx_clineno); #else py_funcname = PyUnicode_FromFormat( "%s (%s:%d)", funcname, __pyx_cfilenm, __pyx_clineno); #endif } else { #if PY_MAJOR_VERSION < 3 py_funcname = PyString_FromString(funcname); #else py_funcname = PyUnicode_FromString(funcname); #endif } if (!py_funcname) goto bad; py_globals = PyModule_GetDict(__pyx_m); if (!py_globals) goto bad; #if PY_MAJOR_VERSION < 3 empty_string = PyString_FromStringAndSize("", 0); #else empty_string = PyBytes_FromStringAndSize("", 0); #endif if (!empty_string) goto bad; py_code = PyCode_New( 0, /*int argcount,*/ #if PY_MAJOR_VERSION >= 3 0, /*int kwonlyargcount,*/ #endif 0, /*int nlocals,*/ 0, /*int stacksize,*/ 0, /*int flags,*/ empty_string, /*PyObject *code,*/ __pyx_empty_tuple, /*PyObject *consts,*/ __pyx_empty_tuple, /*PyObject *names,*/ __pyx_empty_tuple, /*PyObject *varnames,*/ __pyx_empty_tuple, /*PyObject *freevars,*/ __pyx_empty_tuple, /*PyObject *cellvars,*/ py_srcfile, /*PyObject *filename,*/ py_funcname, /*PyObject *name,*/ __pyx_lineno, /*int firstlineno,*/ empty_string /*PyObject *lnotab*/ ); if (!py_code) goto bad; py_frame = PyFrame_New( PyThreadState_GET(), /*PyThreadState *tstate,*/ py_code, /*PyCodeObject *code,*/ py_globals, /*PyObject *globals,*/ 0 /*PyObject *locals*/ ); if (!py_frame) goto bad; py_frame->f_lineno = __pyx_lineno; PyTraceBack_Here(py_frame); bad: Py_XDECREF(py_srcfile); Py_XDECREF(py_funcname); Py_XDECREF(empty_string); Py_XDECREF(py_code); Py_XDECREF(py_frame); } /* Type Conversion Functions */ static INLINE Py_ssize_t __pyx_PyIndex_AsSsize_t(PyObject* b) { Py_ssize_t ival; PyObject* x = PyNumber_Index(b); if (!x) return -1; ival = PyInt_AsSsize_t(x); Py_DECREF(x); return ival; } static INLINE int __Pyx_PyObject_IsTrue(PyObject* x) { if (x == Py_True) return 1; else if (x == Py_False) return 0; else return PyObject_IsTrue(x); } static INLINE PY_LONG_LONG __pyx_PyInt_AsLongLong(PyObject* x) { if (PyInt_CheckExact(x)) { return PyInt_AS_LONG(x); } else if (PyLong_CheckExact(x)) { return PyLong_AsLongLong(x); } else { PY_LONG_LONG val; PyObject* tmp = PyNumber_Int(x); if (!tmp) return (PY_LONG_LONG)-1; val = __pyx_PyInt_AsLongLong(tmp); Py_DECREF(tmp); return val; } } static INLINE unsigned PY_LONG_LONG __pyx_PyInt_AsUnsignedLongLong(PyObject* x) { if (PyInt_CheckExact(x)) { long val = PyInt_AS_LONG(x); if (unlikely(val < 0)) { PyErr_SetString(PyExc_TypeError, "Negative assignment to unsigned type."); return (unsigned PY_LONG_LONG)-1; } return val; } else if (PyLong_CheckExact(x)) { return PyLong_AsUnsignedLongLong(x); } else { PY_LONG_LONG val; PyObject* tmp = PyNumber_Int(x); if (!tmp) return (PY_LONG_LONG)-1; val = __pyx_PyInt_AsUnsignedLongLong(tmp); Py_DECREF(tmp); return val; } } static INLINE unsigned char __pyx_PyInt_unsigned_char(PyObject* x) { if (sizeof(unsigned char) < sizeof(long)) { long long_val = __pyx_PyInt_AsLong(x); unsigned char val = (unsigned char)long_val; if (unlikely((val != long_val) || (long_val < 0))) { PyErr_SetString(PyExc_OverflowError, "value too large to convert to unsigned char"); return (unsigned char)-1; } return val; } else { return __pyx_PyInt_AsLong(x); } } static INLINE unsigned short __pyx_PyInt_unsigned_short(PyObject* x) { if (sizeof(unsigned short) < sizeof(long)) { long long_val = __pyx_PyInt_AsLong(x); unsigned short val = (unsigned short)long_val; if (unlikely((val != long_val) || (long_val < 0))) { PyErr_SetString(PyExc_OverflowError, "value too large to convert to unsigned short"); return (unsigned short)-1; } return val; } else { return __pyx_PyInt_AsLong(x); } } static INLINE char __pyx_PyInt_char(PyObject* x) { if (sizeof(char) < sizeof(long)) { long long_val = __pyx_PyInt_AsLong(x); char val = (char)long_val; if (unlikely((val != long_val) )) { PyErr_SetString(PyExc_OverflowError, "value too large to convert to char"); return (char)-1; } return val; } else { return __pyx_PyInt_AsLong(x); } } static INLINE short __pyx_PyInt_short(PyObject* x) { if (sizeof(short) < sizeof(long)) { long long_val = __pyx_PyInt_AsLong(x); short val = (short)long_val; if (unlikely((val != long_val) )) { PyErr_SetString(PyExc_OverflowError, "value too large to convert to short"); return (short)-1; } return val; } else { return __pyx_PyInt_AsLong(x); } } static INLINE int __pyx_PyInt_int(PyObject* x) { if (sizeof(int) < sizeof(long)) { long long_val = __pyx_PyInt_AsLong(x); int val = (int)long_val; if (unlikely((val != long_val) )) { PyErr_SetString(PyExc_OverflowError, "value too large to convert to int"); return (int)-1; } return val; } else { return __pyx_PyInt_AsLong(x); } } static INLINE long __pyx_PyInt_long(PyObject* x) { if (sizeof(long) < sizeof(long)) { long long_val = __pyx_PyInt_AsLong(x); long val = (long)long_val; if (unlikely((val != long_val) )) { PyErr_SetString(PyExc_OverflowError, "value too large to convert to long"); return (long)-1; } return val; } else { return __pyx_PyInt_AsLong(x); } } static INLINE signed char __pyx_PyInt_signed_char(PyObject* x) { if (sizeof(signed char) < sizeof(long)) { long long_val = __pyx_PyInt_AsLong(x); signed char val = (signed char)long_val; if (unlikely((val != long_val) )) { PyErr_SetString(PyExc_OverflowError, "value too large to convert to signed char"); return (signed char)-1; } return val; } else { return __pyx_PyInt_AsLong(x); } } static INLINE signed short __pyx_PyInt_signed_short(PyObject* x) { if (sizeof(signed short) < sizeof(long)) { long long_val = __pyx_PyInt_AsLong(x); signed short val = (signed short)long_val; if (unlikely((val != long_val) )) { PyErr_SetString(PyExc_OverflowError, "value too large to convert to signed short"); return (signed short)-1; } return val; } else { return __pyx_PyInt_AsLong(x); } } static INLINE signed int __pyx_PyInt_signed_int(PyObject* x) { if (sizeof(signed int) < sizeof(long)) { long long_val = __pyx_PyInt_AsLong(x); signed int val = (signed int)long_val; if (unlikely((val != long_val) )) { PyErr_SetString(PyExc_OverflowError, "value too large to convert to signed int"); return (signed int)-1; } return val; } else { return __pyx_PyInt_AsLong(x); } } static INLINE signed long __pyx_PyInt_signed_long(PyObject* x) { if (sizeof(signed long) < sizeof(long)) { long long_val = __pyx_PyInt_AsLong(x); signed long val = (signed long)long_val; if (unlikely((val != long_val) )) { PyErr_SetString(PyExc_OverflowError, "value too large to convert to signed long"); return (signed long)-1; } return val; } else { return __pyx_PyInt_AsLong(x); } } static INLINE long double __pyx_PyInt_long_double(PyObject* x) { if (sizeof(long double) < sizeof(long)) { long long_val = __pyx_PyInt_AsLong(x); long double val = (long double)long_val; if (unlikely((val != long_val) )) { PyErr_SetString(PyExc_OverflowError, "value too large to convert to long double"); return (long double)-1; } return val; } else { return __pyx_PyInt_AsLong(x); } } 

Una observación: según la evaluación comparativa realizada por los desarrolladores de pybindgen, no hay una diferencia significativa entre boost.python y swig. No he hecho mi propia evaluación comparativa para verificar cuánto de esto depende del uso adecuado de la funcionalidad boost.python.

Tenga en cuenta también que puede haber una razón por la que pybindgen parece ser, en general, bastante más rápido que swig and boost.python: puede que no produzca una unión tan versátil como las otras dos. Por ejemplo, la propagación de excepciones, la verificación del tipo de argumento de llamada, etc. Todavía no he tenido la oportunidad de usar pybindgen, pero tengo la intención de hacerlo.

En general, Boost es un paquete bastante grande para instalar, y la última vez que vi que no puede simplemente instalar boost python, necesita prácticamente toda la biblioteca de Boost. Como otros han mencionado, la comstackción será lenta debido al uso intensivo de la progtwigción de la plantilla, lo que también significa mensajes de error típicamente crípticos en el momento de la comstackción.

Resumen: dada la facilidad de instalación y uso de SWIG, que genera un enlace decente que es robusto y versátil, y que un solo archivo de interfaz permite que su DLL de C ++ esté disponible desde varios otros lenguajes como LUA, C # y Java. es sobre boost.python. Pero a menos que realmente necesite soporte multi-idioma, echaría un vistazo de cerca a PyBindGen debido a su supuesta velocidad, y prestaría mucha atención a la robustez y la versatilidad de enlace que genera.

Aquí hay dragones. No traguen, no aumenten. Para cualquier proyecto complicado, el código que debe completar usted mismo para hacer que funcionen se vuelve inmanejable rápidamente. Si es una API de C simple para su biblioteca (sin clases), puede usar ctypes. Será fácil e indoloro, y no tendrá que pasar horas recorriendo la documentación de estos proyectos de envoltorios laberínticos para encontrar la única nota sobre la función que necesita.

Como le preocupa la velocidad y la sobrecarga, le sugiero que considere PyBindGen .

Tengo experiencia en usarlo para envolver una gran biblioteca interna de C ++. Después de probar SWIG, SIP y Boost.Python, prefiero PyBindGen por los siguientes motivos:

  1. Un envoltorio de PyBindGen es puro Python, no es necesario aprender otro formato de archivo
  2. PyBindGen genera llamadas de API Python C directamente, no hay una capa de direccionamiento indirecto que roba velocidad como SWIG.
  3. El código C generado es limpio y fácil de entender. También me gusta Cython, pero tratar de leer su salida en C puede ser difícil a veces.
  4. Los contenedores de secuencias STL son compatibles (usamos una gran cantidad de std :: vector’s)

Si no es una extensión grande, boost :: python también podría ser una opción, se ejecuta más rápido que un gulp, porque controlas lo que está sucediendo, pero demorará más en dev.

De todos modos, los gastos generales de Swig son aceptables si la cantidad de trabajo dentro de una sola llamada es lo suficientemente grande. Por ejemplo, si lo tuyo es que tienes un bloque lógico de tamaño mediano que quieres mover a C / C ++, pero ese bloque se llama dentro de un bucle cerrado, con frecuencia, es posible que tengas que evitar un gulp, pero realmente no puedo pensar de cualquier ejemplo del mundo real, excepto los sombreadores gráficos con guión.

Antes de renunciar a su código python, eche un vistazo a ShedSkin . Afirman un mejor rendimiento que Psyco en algunos códigos (y también afirman que aún es experimental).

De lo contrario, hay varias opciones para vincular el código C / C ++ a python.

Boost es largo de comstackr, pero es realmente la solución más flexible y fácil de usar.

Nunca he usado SWIG, pero en comparación con boost, no es tan flexible como su marco de enlace genérico, no es un marco dedicado a python.

La siguiente opción es Pyrex . Permite escribir código pseudo python que se comstack como una extensión C.