Extension de python-à swig, pas à swig ou Cython

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

avec l'aide de swig vous n'avez presque pas besoin de se soucier des arguments, etc. Tout fonctionne très bien.

maintenant ma question: swig crée un PY-file assez grand qui fait beaucoup de 'checking' et 'PySwigObject' avant d'appeler le réel .pyd ou .donc le code.

est-ce que l'un de vous a découvrez s'il y a d'autres performances à gagner si vous écrivez ce fichier à la main ou si vous laissez swig le faire.

59
demandé sur JBentley 2009-01-19 11:32:06

9 réponses

bien sûr, vous aurez toujours un gain de performance en faisant cela à 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 aurez besoin de maintenir l'interface à la main, et ce n'est pas une option si votre module est grande!

vous avez fait la bonne chose de choisir d'utiliser un langage de script parce que vous vouliez un développement rapide. De cette façon, vous avez évité le début le syndrome d'optimisation, et maintenant vous voulez optimiser les parties de goulot d'étranglement, génial! Mais si vous faites l'interface c/python à la main, vous tomberez 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 à partir de python avec cstruct .

tenir également compte de la Cython si vous souhaitez utiliser uniquement python code dans votre programme.

25
répondu Mapad 2011-11-24 16:26:10

vous devriez considérer Boost.Python si vous ne prévoyez pas de générer des liaisons pour d'autres langues, 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 faire les reliures.

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


Edit:

peut-être que j'ai besoin d'être plus explicite sur les pour et les contre.

  • Gorgée:

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

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

    j'étais également nécessaire pour traiter l'API C-Python de temps en temps pour (pas si) des conversions de type compliquées.

    Je ne l'utilise plus.

  • coup de pouce.Python:

    pro: C'est un très complète de la bibliothèque. Il vous permet de faire presque tout ce qui est possible avec L'API C, mais en C++. Je n'ai jamais eu à écrire C-API code avec cette bibliothèque. Je n'ai jamais rencontré de bogue à cause de la bibliothèque. Le Code pour les reliures fonctionne comme un charme ou refuse de compiler.

    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'essaierai probablement avec Cython.

    inconvénients: si vous n'avez pas de Boost précompilé.Python library vous allez utiliser Bjam (sorte de faire remplacement). Je 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 un lot de temps pour les compiler.

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

    cons: seulement le des problèmes en raison de coup de pouce.Python lui-même. Mise à jour: à partir de 2014, ce projet semble désormais abandonné.

  • Pybindgen:

    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 écrite 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 limites: vous ne pouvez pas utiliser l'héritage multiple pour vos classes C++, Les Callbacks (pas automatiquement, le code personnalisé de gestion de callback peut être écrit, cependant). 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 paquet pour interfacer le code C / C++ avec python. Il est basé sur des ctypes. Je ne l'ai pas essayé déjà, mais je vais le faire! Note: ce projet semble mécontent, comme Py++.

  • CFFI : Je ne connaissais pas l'existence de celui-ci jusqu'à très récemment donc pour l'instant je ne peux pas donner mon opinion. 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. En gros, vous écrivez du code en spécial .custode fichiers. 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 normal (et en fait Python pur est valide .pyx Cython files), mais vous pouvez également plus d'informations comme les types variables. Cette saisie optionnelle permet à Cython de générer plus rapidement du code C. Le Code dans les fichiers Cython peut appeler à la fois des fonctions Python pures mais aussi C et les fonctions C++ (et aussi les méthodes C++).

    il m'a fallu un certain temps pour penser à Cython, que dans le même code appel C et c++ Fonction, Mélange Python et c variables, et ainsi de suite. Mais c'est une langue très puissante, avec une communauté active (en 2014) et amicale.

58
répondu ascobol 2014-07-07 22:33:31

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

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

SWIG utilisé pour être le plus lent. Avec L'option new-builtin, SWIG semble être le plus rapide.

27
répondu user763305 2011-07-14 10:21:51

en utilisant Cython est assez bon. Vous pouvez écrire votre extension C avec une syntaxe de type Python et lui faire générer du code C. Standard inclus. Puisque vous avez le code déjà en python, vous devez faire quelques changements à votre goulot d'étranglement de code et de code C sera généré.

exemple. hello.pyx :

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

qui génère 601 lignes du code de la bobine:

/* 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
répondu nosklo 2009-01-19 10:52:49

une observation: sur la base de l'étalonnage effectué 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 l'utilisation correcte du boost.la fonctionnalité python.

noter aussi qu'il peut y avoir une raison que pybindgen semble être en général un peu plus rapide que swig et boost.python: il mai ne produit pas aussi polyvalent la liaison que les deux autres. Par exemple, la propagation d'exception, la vérification de 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 gros paquet à installer, et la dernière fois que j'ai vu que vous ne pouvez pas juste installer boost python vous avez besoin de la bibliothèque Boost complète. Comme d'autres l'ont mentionné, la compilation sera lente en raison de l'utilisation intensive de la programmation de template, ce qui signifie aussi typiquement des messages d'erreur plutôt cryptés au moment de la compilation.

résumé: étant donné la facilité D'installation et d'utilisation de SWIG, le fait qu'il génère une reliure décente qui est robuste et polyvalente, et qu'un fichier d'interface permet à votre DLL C++ d'être disponible dans plusieurs autres langues comme LUA, C#, et Java, je le préférerais à boost.Python. Mais à moins que vous n'ayez vraiment besoin d'un support multi-langues, Je voudrais regarder de près PyBindGen en raison de sa vitesse supposée, et faire attention à la robustesse et la polyvalence de liaison qu'il génère.

7
répondu schollii 2010-07-02 15:58:44

il y a des dragons ici. Ne gigotez pas, ne boostez pas. Pour tout projet compliqué le code que vous devez remplir vous-même pour les faire fonctionner devient rapidement ingérable. Si C'est une simple API C pour votre bibliothèque (Pas de classes), vous pouvez simplement utiliser les ctypes. Ce sera facile et sans douleur, et vous n'aurez pas à passer des heures à parcourir la documentation de ces projets labyrinthine wrapper en essayant de trouver la petite note sur la fonctionnalité dont vous avez besoin.

5
répondu Jorenko 2009-01-20 13:56:30

puisque vous vous souciez de la vitesse et du plafond, je suggère de considérer PyBindGen .

j'ai l'expérience de 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 empaquetage PyBindGen est pur-Python, pas besoin d'apprendre un autre format de fichier
  2. PyBindGen génère des appels D'API Python C directement, là il n'y a pas de couche de déviation indirecte comme SWIG.
  3. le code C généré est propre et simple à comprendre. J'aime Cython aussi, mais essayer de lire sa sortie C peut être difficile à certains moments.
  4. STL conteneurs de séquence sont pris en charge (nous utilisons beaucoup de std::vector)
5
répondu sstock 2009-01-26 05:07:53

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

de toute façon rasade de surcharge est acceptable si la quantité de travail à l'intérieur d'un seul appel est assez grand. Par exemple, si vous avez un bloc logique de taille moyenne que vous voulez passer en C/C++, mais ce bloc est appelé dans une boucle serrée, fréquemment, vous pourriez avoir à éviter swig, mais je Je ne peux pas vraiment penser à des exemples du monde réel sauf pour les shaders graphiques scriptés.

3
répondu Robert Gould 2009-01-19 08:34:30

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

sinon, il y a plusieurs choix pour lier le code C/C++ à python.

Boost est long à compiler mais 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 aussi flexible que c'est un cadre de liaison Générique, pas un cadre dédié à python.

le choix suivant est Pyrex . Il permet d'écrire du pseudo code python qui est compilé comme une extension C.

3
répondu Philippe F 2009-01-19 09:19:59