Domanda

Ho trovato il collo di bottiglia nel mio codice Python, ho giocato con Psycho ecc. Poi ho deciso di scrivere un'estensione c / c ++ per le prestazioni.

Con l'aiuto del sorso non hai quasi bisogno di preoccuparti degli argomenti, ecc. Tutto funziona bene.

Ora la mia domanda: swig crea un file py abbastanza grande che esegue molti "controlli" e "PySwigObject" prima di chiamare il vero codice .pyd o .so.

Qualcuno di voi ha qualche esperienza se ci sono altre prestazioni da guadagnare se scrivete a mano questo file o lasciate che il swig lo faccia.

È stato utile?

Soluzione

Sicuramente avrai sempre un guadagno prestazionale facendo questo a mano, ma il guadagno sarà molto piccolo rispetto allo sforzo richiesto per farlo. Non ho alcuna cifra da darti, ma non lo consiglio, perché dovrai mantenere l'interfaccia manualmente e questa non è un'opzione se il tuo modulo è grande!

Hai fatto la cosa giusta per scegliere di usare un linguaggio di scripting perché volevi uno sviluppo rapido. In questo modo hai evitato la sindrome di ottimizzazione precoce e ora vuoi ottimizzare le parti del collo di bottiglia, fantastico! Ma se esegui manualmente l'interfaccia C / python, cadrai sicuramente nella sindrome dell'ottimizzazione precoce.

Se vuoi qualcosa con meno codice di interfaccia, puoi pensare di creare una dll dal tuo codice C e usare quella libreria direttamente da Python con cstruct .

Considera anche Cython se vuoi usare solo codice Python nel tuo programma.

Altri suggerimenti

Dovresti prendere in considerazione Boost.Python se non stai pianificando di generare associazioni per altre lingue e con swig.

Se hai molte funzioni e classi da associare, Py ++ è un ottimo strumento che automaticamente genera il codice necessario per creare i collegamenti.

Pybindgen può anche essere un'opzione, ma è un nuovo progetto e meno completo che Boost .Python.


Modifica:

Forse dovrei essere più esplicito su pro e contro.

  • Swig:

    pro: puoi generare associazioni per molti linguaggi di scripting.

    contro: non mi piace il modo in cui funziona il parser. Non so se i progressi siano stati fatti, ma due anni fa il parser C ++ era piuttosto limitato. Il più delle volte ho dovuto copiare / incollare le mie intestazioni .h per aggiungere alcuni caratteri % e dare ulteriori suggerimenti al parser di swig.

    Avevo anche bisogno di occuparmi di tanto in tanto della C-API di Python per conversioni di tipo (non così) complicate.

    Non lo sto più usando.

  • Boost.Python:

    pro: È una biblioteca molto completa. Ti consente di fare quasi tutto ciò che è possibile con l'API C, ma in C ++. Non ho mai dovuto scrivere codice C-API con questa libreria. Inoltre non ho mai riscontrato bug a causa della libreria. Il codice per gli attacchi funziona come un incantesimo o rifiuta la compilazione.

    È probabilmente una delle migliori soluzioni attualmente disponibili se hai già qualche libreria C ++ da associare. Ma se hai solo una piccola funzione C da riscrivere, probabilmente proverei con Cython.

    contro: se non si dispone di una libreria Boost.Python precompilata, si utilizzerà Bjam (una sorta di sostituzione sostitutiva). Odio davvero Bjam e la sua sintassi.

    Le librerie Python create con B.P tendono a diventare obese. Ci vuole anche un lotto di tempo per compilarli.

  • Py ++ (fuori produzione): Boost.Python è facile. Py ++ utilizza un parser C ++ per leggere il codice e quindi genera automaticamente il codice Boost.Python. Hai anche un grande supporto dal suo autore (no, non sono io ;-)).

    contro: solo i problemi dovuti a Boost.Python stesso. Aggiornamento: dal 2014 questo progetto ora sembra interrotto.

  • Pybindgen:

    Genera il codice relativo alla C-API. Puoi descrivere funzioni e classi in un file Python o lasciare che Pybindgen legga le tue intestazioni e generi automaticamente i collegamenti (per questo usa pygccxml, una libreria python scritta dall'autore di Py ++).

    contro: è un progetto giovane, con un team più piccolo di Boost.Python. Ci sono ancora alcune limitazioni: non è possibile utilizzare l'ereditarietà multipla per le classi C ++, Callbacks (non automaticamente, tuttavia è possibile scrivere un codice personalizzato per la gestione delle callback). Traduzione delle eccezioni di Python in C.

    Vale sicuramente la pena dare un'occhiata.

  • Uno nuovo: Il 20/01/2009 l'autore di Py ++ ha annunciato un nuovo pacchetto per l'interfacciamento del codice C / C ++ con Python. Si basa sui tipi. Non l'ho già provato ma lo farò! Nota: questo progetto sembra sconcertato, come Py ++.

  • CFFI : non sapevo l'esistenza di questo fino a poco tempo fa, quindi per ora io non posso dare la mia opinione. Sembra che tu possa definire le funzioni C nelle stringhe Python e chiamarle direttamente dallo stesso modulo Python.

  • Cython : questo è il metodo che sto attualmente usando nei miei progetti. Fondamentalmente scrivi il codice in speciali file .pyx. Tali file vengono compilati (tradotti) in codice C che a loro volta vengono compilati in moduli CPython. Il codice Cython può apparire come un normale Python (e in effetti Python puro sono file Cypon .pyx validi), ma puoi anche m

SWIG 2.0.4 ha introdotto una nuova opzione -builtin che migliora le prestazioni. Ho fatto alcuni benchmarking usando un programma di esempio che fa molte chiamate veloci a un'estensione C ++. Ho creato l'estensione usando boost.python, PyBindGen, SIP e SWIG con e senza l'opzione -builtin. Ecco i risultati (media di 100 esecuzioni):

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

SWIG era il più lento. Con la nuova opzione -builtin, SWIG sembra essere il più veloce.

L'uso di Cython è abbastanza buono. Puoi scrivere l'estensione C con una sintassi simile a Python e fare in modo che generi codice C. Piastra della caldaia inclusa. Dato che hai già il codice in Python, devi solo apportare alcune modifiche al tuo collo di bottiglia e il codice C verrà generato da esso.

Esempio. hello.pyx :

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

Questo genera 601 righe di codice del bollettino:

/* 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 <math.h>
#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);
    }
}

Un'osservazione: in base al benchmarking condotto dagli sviluppatori di pybindgen, non vi è alcuna differenza significativa tra boost.python e swig. Non ho eseguito il mio benchmarking per verificare quanto ciò dipenda dall'uso corretto della funzionalità boost.python.

Nota anche che potrebbe esserci una ragione per cui pybindgen sembra essere in generale un po 'più veloce di swig e boost.python: potrebbe non produrre un legame così versatile come gli altri due. Ad esempio, propagazione di eccezioni, controllo del tipo di argomento di chiamata, ecc. Non ho ancora avuto la possibilità di usare pybindgen ma ho intenzione di farlo.

Boost è in genere un pacchetto abbastanza grande da installare, e l'ultima volta che ho visto che non puoi semplicemente installare boost python hai praticamente bisogno dell'intera libreria Boost. Come altri hanno già detto, la compilazione sarà lenta a causa dell'uso pesante della programmazione dei template, il che significa anche messaggi di errore piuttosto criptici al momento della compilazione.

Riepilogo: data la facilità con cui SWIG è facile da installare e utilizzare, che genera un'associazione decente che è robusta e versatile e che un file di interfaccia consente alla DLL C ++ di essere disponibile da diverse altre lingue come LUA, C # e Java, Lo preferirei a boost.python. Ma a meno che tu non abbia davvero bisogno del supporto multilingue, darei un'occhiata da vicino a PyBindGen per la sua presunta velocità e presterei molta attenzione alla robustezza e alla versatilità del legame che genera.

Dal momento che sei preoccupato per la velocità e le spese generali, ti suggerisco di prendere in considerazione PyBindGen .

Ho esperienza nell'usarlo per avvolgere una grande libreria C ++ interna. Dopo aver provato SWIG, SIP e Boost.Python preferisco PyBindGen per i seguenti motivi:

  1. Un wrapper PyBindGen è puro Python, non è necessario imparare un altro formato di file
  2. PyBindGen genera direttamente le chiamate all'API Python C, non esiste un livello di indirizzamento indiretto alla velocità come SWIG.
  3. Il codice C generato è pulito e semplice da capire. Mi piace anche Cython, ma a volte provare a leggere il suo output in C può essere difficile.
  4. I contenitori di sequenze STL sono supportati (usiamo molti std :: vector's)

Ci sono draghi qui. Non nuotare, non aumentare. Per qualsiasi progetto complicato il codice che devi compilare per farli funzionare diventa rapidamente ingestibile. Se si tratta di un'API C semplice per la tua libreria (senza classi), puoi semplicemente usare i tipi. Sarà facile e indolore e non dovrai passare ore a scorrere la documentazione per questi progetti di wrapper labirintici cercando di trovare l'unica piccola nota sulla funzione di cui hai bisogno.

Se non è una grande estensione, boost :: python potrebbe anche essere un'opzione, si esegue più velocemente del swig, perché controlli cosa sta succedendo, ma ci vorrà più tempo per lo sviluppo.

In ogni caso l'overhead del swig è accettabile se la quantità di lavoro all'interno di una singola chiamata è abbastanza grande. Ad esempio, se il problema è che si dispone di un blocco logico di medie dimensioni che si desidera spostare in C / C ++, ma quel blocco viene chiamato in un ciclo stretto, spesso, potrebbe essere necessario evitare il sorso, ma non riesco davvero a pensare di tutti gli esempi del mondo reale ad eccezione degli shader di grafica con script.

Prima di rinunciare al tuo codice Python, dai un'occhiata a ShedSkin . Affermano prestazioni migliori di Psyco su alcuni codici (e affermano anche che è ancora sperimentale).

Altrimenti, ci sono diverse opzioni per associare il codice C / C ++ a Python.

Boost è lungo da compilare ma è davvero la soluzione più flessibile e facile da usare.

Non ho mai usato SWIG ma rispetto a boost, non è flessibile come è il framework di binding generico, non un framework dedicato a Python.

La prossima scelta è Pyrex . Permette di scrivere codice pseudo python che viene compilato come estensione C.

C'è un articolo che vale la pena leggere sull'argomento Cython, pybind11, cffi & # 8211; quale strumento dovresti scegliere?

Riepilogo rapido per gli impazienti:

  • Cython compila il tuo pitone in C / C ++ permettendoti di incorporare il tuo C / C ++ nel codice python. Utilizza l'associazione statica. Per programmatori python.

  • pybind11 (e boost.python) è l'opposto. Associa le tue cose in fase di compilazione dal lato C ++. Per programmatori C ++.

  • CFFI ti consente di associare dinamicamente i contenuti nativi in ??fase di esecuzione. Semplice da usare, ma con penalità di prestazione più elevate.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top