Question

J'ai trouvé le goulot d'étranglement dans mon code python, joué avec psycho, etc. Ensuite, j'ai décidé d'écrire une extension c / c ++ pour la performance.

Avec l'aide de swig, vous n'avez presque plus besoin de vous soucier des arguments, etc. Tout fonctionne bien.

Maintenant ma question: swig crée un fichier py assez volumineux qui effectue beaucoup de "vérifications" et de "PySwigObject" avant d'appeler le code .pyd ou .so actuel.

Quelqu'un d’entre vous at-il une quelconque expérience des performances à gagner en écrivant à la main ce fichier ou en laissant swig le faire?

Était-ce utile?

La solution

Certes, vous obtiendrez toujours un gain de performance à la main, mais le gain sera très faible comparé aux efforts requis pour ce faire. Je n'ai pas de chiffre à vous donner, mais je ne le recommande pas, car vous devrez entretenir l'interface à la main, et ce n'est pas une option si votre module est volumineux!

Vous avez bien fait d’utiliser un langage de script parce que vous souhaitiez un développement rapide. De cette façon, vous évitez le syndrome d'optimisation précoce et vous souhaitez maintenant optimiser les composants goulot d'étranglement, c'est parfait! Mais si vous faites l’interface C / python à la main, vous tomberez certainement dans le syndrome d’optimisation précoce.

Si vous voulez quelque chose avec moins de code d'interface, vous pouvez créer une dll à partir de votre code C et utiliser cette bibliothèque directement à partir de python avec cstruct .

Pensez également à Cython si vous souhaitez utiliser uniquement du code python dans votre programme.

Autres conseils

Vous devriez envisager Boost.Python si vous ne prévoyez pas de générer des liaisons pour d’autres langues également avec swig.

Si vous avez beaucoup de fonctions et de classes à associer, Py ++ est un excellent outil qui permet automatiquement génère le code nécessaire pour effectuer les liaisons.

Pybindgen peut également être une option, mais il s'agit d'un nouveau projet moins complet que Boost. .Python.

Modifier:

Peut-être dois-je être plus explicite sur les avantages et les inconvénients.

  • Swig:

    pro: vous pouvez générer des liaisons pour de nombreux langages de script.

    inconvénients: je n'aime pas la façon dont fonctionne l'analyseur. Je ne sais pas si certains progrès ont été réalisés, mais il y a deux ans, l'analyseur C ++ était assez limité. La plupart du temps, je devais copier / coller mes en-têtes .h, ajouter des caractères % et donner des conseils supplémentaires à l'analyseur swig.

    J'étais également obligé de traiter de temps en temps avec la C-API Python pour des conversions de type compliquées.

    Je ne l'utilise plus.

  • Boost.Python:

    pro: C'est une bibliothèque très complète. Cela vous permet de faire presque tout ce qui est possible avec C-API, mais en C ++. Je n'ai jamais eu à écrire de code C-API avec cette bibliothèque. J'ai aussi jamais rencontré de bug en raison de la bibliothèque. Le code pour les liaisons fonctionne comme un charme ou refuse la compilation.

    C'est probablement l'une des meilleures solutions actuellement disponibles si vous avez déjà une bibliothèque C ++ à lier. Mais si vous n’avez qu’une petite fonction en C à réécrire, j’essaierais probablement avec Cython.

    inconvénients: si vous n'avez pas de bibliothèque Boost.Python pré-compilée, vous utiliserez Bjam (sorte de remplacement). Je déteste vraiment Bjam et sa syntaxe.

    Les bibliothèques Python créées avec B.P ont tendance à devenir obèses. Il faut aussi beaucoup de temps pour les compiler.

  • Py ++ (discontinued): c'est Boost.Python rendu facile. Py ++ utilise un analyseur C ++ pour lire votre code, puis génère automatiquement le code Boost.Python. Vous bénéficiez également d’un excellent soutien de la part de son auteur (non ce n’est pas moi ;-)).

    inconvénients: seuls les problèmes dus à Boost.Python lui-même. Mise à jour: à partir de 2014, ce projet semble abandonné.

  • Pybindgen:

    Il génère le code traitant de la C-API. Vous pouvez soit décrire des fonctions et des classes dans un fichier Python, soit laisser Pybindgen lire vos en-têtes et générer des liaisons automatiquement (il utilise pour cela pygccxml, une bibliothèque python écrite par l'auteur de Py ++).

    inconvénients: c'est un projet jeune, avec une équipe plus petite que Boost.Python. Il y a toujours des limitations: vous ne pouvez pas utiliser l'héritage multiple pour vos classes C ++, Callbacks (pas automatiquement, un code de traitement des callbacks personnalisés peut être écrit). Traduction des exceptions Python en C.

    Cela vaut vraiment le coup d'oeil.

  • Un nouveau: Le 2009/01/20, l’auteur de Py ++ a annoncé un nouveau package pour interfacer le code C / C ++ avec python. Il est basé sur des types. Je ne l'ai pas déjà essayé mais je le ferai! Remarque: ce projet a l’air discret, en tant que Py ++.

  • CFFI : Je ne connaissais pas l'existence de celui-ci jusqu'à tout récemment, c'est pourquoi je ne peux pas donner mon opinion. Il semble que vous puissiez définir des fonctions C dans des chaînes Python et les appeler directement à partir du même module Python.

  • Cython : il s'agit de la méthode que j'utilise actuellement dans mes projets. En gros, vous écrivez du code dans des fichiers spéciaux .pyx. Ces fichiers sont compilés (traduits) en code C qui, à leur tour, sont compilés en modules CPython. Le code Cython peut ressembler à du Python normal (et en fait, les Python purs sont des fichiers .pyx Cython valides), mais vous pouvez aussi

SWIG 2.0.4 a introduit une nouvelle option -builtin qui améliore les performances. J'ai effectué des analyses comparatives à l'aide d'un exemple de programme qui effectue beaucoup d'appels rapides vers une extension C ++. J'ai construit l'extension à l'aide de boost.python, PyBindGen, SIP et SWIG avec et sans l'option -builtin. Voici les résultats (moyenne de 100 analyses):

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

Auparavant, SWIG était le plus lent. Avec la nouvelle option -builtin, SWIG semble être le plus rapide.

L'utilisation de Cython est très bonne. Vous pouvez écrire votre extension C avec une syntaxe semblable à celle de Python et la faire générer du code C. Plaque de cuisson inclus. Puisque vous avez déjà le code en python, vous devez apporter quelques modifications à votre code de goulot d’étranglement et le code C en sera généré.

Exemple. hello.pyx :

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

Cela génère 601 lignes de code passe-partout:

/* 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 constat: sur la base du benchmarking réalisé par les développeurs de pybindgen, il n’existe aucune différence significative entre boost.python et swig. Je n'ai pas fait mes propres analyses comparatives pour vérifier à quel point cela dépend de l'utilisation correcte de la fonctionnalité boost.python.

Notez également qu’il pourrait y avoir une raison pour que pybindgen semble être en général un peu plus rapide que swig et boost.python: il se peut qu’il ne produise pas une liaison aussi polyvalente que les deux autres. Par exemple, la propagation des exceptions, la vérification du type d'argument d'appel, etc. Je n'ai pas encore eu l'occasion d'utiliser pybindgen mais j'en ai l'intention.

Boost est en général un paquet assez volumineux à installer, et la dernière fois que j'ai vu, vous ne pouvez pas installer simplement boost python, vous avez plutôt besoin de toute la bibliothèque Boost. Comme d'autres l'ont mentionné, la compilation sera lente en raison de l'utilisation intensive de la programmation par modèle, ce qui signifie également des messages d'erreur plutôt cryptiques au moment de la compilation.

Résumé: compte tenu de la facilité d'installation et d'utilisation de SWIG, il génère une liaison fiable, robuste et polyvalente, et un fichier d'interface permet à votre DLL C ++ d'être disponible à partir de plusieurs autres langages tels que LUA, C # et Java. Je préférerais cela à boost.python. Mais à moins que vous n'ayez vraiment besoin d'une assistance multilingue, je regarderais de près PyBindGen en raison de sa vitesse supposée, et porterais une attention particulière à la robustesse et à la polyvalence de la liaison qu'il génère.

Puisque vous êtes préoccupé par la rapidité et les frais généraux, je vous suggère d’envisager PyBindGen .

J'ai l'habitude de l'utiliser pour envelopper une grande bibliothèque interne C ++. Après avoir essayé SWIG, SIP et Boost.Python, je préfère PyBindGen pour les raisons suivantes:

  1. Un wrapper PyBindGen est du pur-Python, inutile d'apprendre un autre format de fichier
  2. PyBindGen génère directement les appels de l’API C Python. Il n’existe pas de couche indirectionnelle destructrice de vitesse comme SWIG.
  3. Le code C généré est propre et simple à comprendre. J'aime aussi Cython, mais essayer de lire sa sortie en C peut parfois être difficile.
  4. Les conteneurs de séquence STL sont supportés (nous utilisons beaucoup de std :: vector)

Il y a des dragons ici. Ne ratez pas, ne boostez pas. Pour tout projet compliqué, le code que vous devez renseigner vous-même pour que leur travail devienne ingérable devient rapidement gérable. S'il s'agit d'une API C simple pour votre bibliothèque (pas de classes), vous pouvez simplement utiliser ctypes. Ce sera facile et sans douleur, et vous n'aurez pas à passer des heures à parcourir la documentation de ces projets d'enveloppes labyrinthiques en essayant de trouver la toute petite note sur la fonctionnalité dont vous avez besoin.

Si ce n'est pas une grande extension, boost :: python peut aussi être une option, il s'exécute plus rapidement que swig, car vous contrôlez ce qui se passe, mais cela prendra plus de temps à devenir.

De toute façon, les frais généraux de swig sont acceptables si la quantité de travail au cours d’un seul appel est suffisamment importante. Par exemple, si votre problème est que vous souhaitez déplacer un bloc logique de taille moyenne en C / C ++, mais que ce bloc est appelé dans une boucle étroite, vous devrez peut-être souvent éviter la swig, mais je ne peux vraiment pas penser. de tous les exemples du monde réel, sauf pour les shaders graphiques scriptés.

Avant d'abandonner votre code python, consultez ShedSkin . Ils revendiquent de meilleures performances que Psyco sur certains codes (et déclarent également qu’il est encore expérimental).

Sinon, il existe plusieurs choix pour lier le code C / C ++ à python.

Boost est long à compiler mais est vraiment la solution la plus flexible et la plus facile à utiliser.

Je n’ai jamais utilisé SWIG, mais comparé à boost, il n’est pas aussi flexible que son cadre de liaison générique, pas un framework dédié à python.

Le choix suivant est Pyrex . Il permet d’écrire du code pseudo python compilé en extension C.

Il y a un article à lire sur le sujet Cython, pybind11, cffi & # 8211; quel outil choisir?

Récapitulatif rapide pour les impatients:

  • Cython compile votre python en C / C ++, ce qui vous permet d'incorporer votre C / C ++ dans du code python. Utilise la liaison statique. Pour les programmeurs python.

  • pybind11 (et boost.python) est l'inverse. Liez vos données à la compilation du côté C ++. Pour les programmeurs C ++.

  • CFFI vous permet de lier le contenu natif de manière dynamique au moment de l'exécution. Simple à utiliser, mais plus pénalisant en termes de performances.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top