web-dev-qa-db-fra.com

Étendre python - à swig, pas à swig ou Cython

J'ai trouvé le goulot d'étranglement dans mon python, joué avec psycho etc., puis j'ai décidé d'écrire une extension c/c ++ pour les performances.

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

Maintenant, ma question: swig crée un assez grand fichier py qui fait beaucoup de "vérifications" et de "PySwigObject" avant d'appeler le code .pyd ou .so réel.

Quelqu'un parmi vous a-t-il une expérience pour savoir s'il y a plus de performances à gagner si vous écrivez à la main ce fichier ou laissez Swig le faire?.

62
RSabet

Bien sûr, vous aurez toujours un gain de performance en le faisant à la main, mais le gain sera très faible par rapport à l'effort requis pour le faire. Je n'ai pas de chiffre à vous donner mais je ne le recommande pas, car vous devrez maintenir l'interface à la main, et ce n'est pas une option si votre module est grand!

Vous avez fait ce qu'il fallait pour choisir d'utiliser un langage de script parce que vous vouliez un développement rapide. De cette façon, vous avez évité le syndrome d'optimisation précoce, et maintenant vous voulez optimiser les pièces de goulot d'étranglement, tant mieux! 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 penser à créer une dll à partir de votre code C, et utiliser cette bibliothèque directement depuis python avec cstruct .

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

26
Mapad

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

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

Pybindgen peut également être une option, mais c'est un nouveau projet et moins complet que Boost.Python.


Éditer:

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

  • Lampée:

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

    inconvénients: je n'aime pas la façon dont l'analyseur fonctionne. Je ne sais pas si cela a fait des progrè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 en ajoutant % caractères et donne des conseils supplémentaires à l'analyseur swig.

    J'avais également besoin de gérer de temps en temps le Python C-API pour des conversions de type compliquées (pas si).

    Je ne l'utilise plus.

  • Boost.Python:

    pro: C'est une bibliothèque très complète. Il vous permet de faire presque tout ce qui est possible avec l'API C, mais en C++. Je n'ai jamais eu à écrire de code C-API avec cette bibliothèque. Je n'ai également jamais rencontré de bogue à cause 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 C à réécrire, j'essaierais probablement avec Cython.

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

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

  • Py ++ (interrompu): c'est Boost.Python rendu facile. Py ++ utilise un analyseur C++ pour lire votre code, puis génère automatiquement du code Boost.Python. Vous avez également un grand soutien de son auteur (non ce n'est pas moi ;-)).

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

  • Pybindgen:

    Il génère le code traitant de l'API C. 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 (pour cela, il utilise pygccxml, une bibliothèque python écrit par l'auteur de Py ++).

    inconvénients: c'est un projet jeune, avec une équipe plus petite que Boost.Python. Il y a encore quelques limitations: vous ne pouvez pas utiliser l'héritage multiple pour vos classes C++, les rappels (pas automatiquement, cependant, un code de gestion de rappel personnalisé peut être écrit). Traduction de Python exceptions à C.

    Ça vaut vraiment le coup d'oeil.

  • Un nouveau: le 20/01/2009, l'auteur de Py ++ a annoncé un nouveau package pour interfacer le code C/C++ avec python. Il est basé sur des ctypes. Je ne l'ai pas encore essayé mais je le ferai! Remarque: ce projet semble décontenancé, comme Py ++.

  • CFFI : Je ne connaissais l'existence de celui-ci que très récemment donc pour l'instant je ne peux pas donner mon avis. Il semble que vous puissiez définir des fonctions C dans les chaînes Python et les appeler directement à partir du même module Python.

  • Cython : C'est la méthode que j'utilise actuellement dans mes projets. Fondamentalement, vous écrivez du code dans des fichiers .pyx spéciaux. Ces fichiers sont compilés (traduits) en code C qui à son tour sont compilés en modules CPython. Le code Cython peut ressembler à un Python (et en fait pur Python sont des fichiers Cython .pyx valides), mais vous pouvez également plus d'informations comme des types de variables. la saisie facultative permet à Cython de générer un code C. plus rapide. Le code dans les fichiers Cython peut appeler à la fois des fonctions Python mais aussi des fonctions C et C++ (ainsi que des méthodes C++).

    Il m'a fallu un certain temps pour penser en Cython, que dans le même appel de code C et fonction C++, mélanger Python et variables C, et ainsi de suite. Mais c'est un langage très puissant, avec un actif (en 2014) et une communauté amicale.

62
ascobol

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

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

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

28
Johan Råde

Utiliser Cython est assez bon. Vous pouvez écrire votre extension C avec une syntaxe de type Python et la faire générer du code C. Plaque chauffante incluse. Puisque vous avez déjà le code en python, vous n'avez qu'à apporter quelques modifications à votre code de goulot d'étranglement et le code C sera généré à partir de celui-ci.

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);
    }
}
16
nosklo

Une observation: sur la base de l'analyse comparative effectuée par les développeurs pybindgen, il n'y a pas de différence significative entre boost.python et swig. Je n'ai pas fait mon propre benchmarking pour vérifier dans quelle mesure cela dépend de la bonne utilisation de la fonctionnalité boost.python.

Notez également qu'il peut y avoir une raison pour laquelle pybindgen semble en général un peu plus rapide que swig et boost.python: il peut ne produit 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 la possibilité d'utiliser pybindgen mais j'en ai l'intention.

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

Résumé: étant donné la facilité d'installation et d'utilisation de SWIG, qu'il génère une liaison décente, robuste et polyvalente, et qu'un fichier d'interface permet à votre C++ DLL d'être disponible dans plusieurs autres langues comme LUA , C # et Java, je le préférerais à boost.python. Mais à moins que vous ayez vraiment besoin d'une prise en charge multilingue, je regarderais de près PyBindGen en raison de sa prétendue vitesse, et je ferais très attention à la robustesse et à la polyvalence des liaisons qu'il génère. .

7
schollii

Puisque vous êtes préoccupé par la vitesse et les frais généraux, je vous suggère de considérer PyBindGen .

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

  1. Un wrapper PyBindGen est pur-Python, pas besoin d'apprendre un autre format de fichier
  2. PyBindGen génère Python C API appelle directement, il n'y a pas de couche d'indirection 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 C peut parfois être difficile.
  4. Les conteneurs de séquences STL sont pris en charge (nous utilisons beaucoup de std :: vector's)
6
sstock

Il y a des dragons ici. Ne pas avaler, ne pas booster. Pour tout projet compliqué, le code que vous devez remplir vous-même pour le faire fonctionner devient rapidement ingérable. Si c'est une API C simple pour votre bibliothèque (pas de classes), vous pouvez simplement utiliser des ctypes. Ce sera facile et indolore, et vous n'aurez pas à passer des heures à parcourir la documentation de ces projets d'emballage labyrinthiques pour essayer de trouver la toute petite note sur la fonctionnalité dont vous avez besoin.

5
Jorenko

Si ce n'est pas une grande extension, boost :: python peut également ê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 à dev.

Quoi qu'il en soit, les frais généraux de swig sont acceptables si la quantité de travail dans un seul appel est suffisamment importante. Par exemple, si vous posez un problème, vous devez déplacer un bloc logique de taille moyenne vers C/C++, mais ce bloc est appelé dans une boucle étroite, fréquemment, vous devrez peut-être éviter la swig, mais je ne peux pas vraiment penser de tous les exemples du monde réel à l'exception des shaders graphiques scriptés.

3
Robert Gould

Avant de renoncer à votre python, jetez un œil à ShedSkin . Ils réclament de meilleures performances que Psyco sur un certain code (et indiquent également qu'il est encore expérimental).

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

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

Je n'ai jamais utilisé SWIG mais par rapport à boost, ce n'est pas aussi flexible que son framework de liaison générique, pas un framework dédié à python.

Le prochain choix est Pyrex . Il permet d'écrire du pseudo python code qui se compile comme une extension C.

3
Philippe F

Il y a un article à lire sur le sujet Cython, pybind11, cffi - quel outil choisir?

Récapitulatif rapide pour les impatients:

  • Cython compile votre python en C/C++ vous permettant d'incorporer votre C/C++ dans python code. Utilise une liaison statique. Pour les programmeurs python.

  • pybind11 (et boost.python) est le contraire. Liez vos trucs au moment de la compilation du côté C++. Pour les programmeurs C++.

  • [~ # ~] cffi [~ # ~] vous permet de lier dynamiquement le contenu natif lors de l'exécution. Simple à utiliser, mais pénalité de performance plus élevée.

0
Thinkeye