2009-01-19 7 views
58

Ich fand den Engpass in meinem Python-Code, spielte mit psycho etc. herum. Dann entschied ich mich, eine c/C++ Erweiterung für die Leistung zu schreiben.Erweitern Python - zu swig, nicht zu swig oder Cython

Mit Hilfe von SWIG müssen Sie fast nicht über Argumente etc. kümmern. Alles funktioniert gut.

Jetzt meine Frage: swig erstellt eine ziemlich große Py-Datei, die eine Menge von 'checkings' und 'PySwigObject' vor dem Aufruf der tatsächlichen .pyd oder .so Code.

Hat jemand von euch irgendeine Erfahrung, ob es etwas mehr Leistung zu gewinnen gibt, wenn Sie diese Datei handschriftlich schreiben oder swig machen lassen.

Antwort

24

Natürlich haben Sie immer einen Leistungszuwachs von Hand, aber der Gewinn ist sehr gering im Vergleich zu dem Aufwand dafür. Ich habe keine Figur, die ich Ihnen geben kann, aber ich empfehle das nicht, weil Sie die Schnittstelle von Hand pflegen müssen, und dies ist keine Option, wenn Ihr Modul groß ist!

Sie haben sich für die Verwendung einer Skriptsprache entschieden, weil Sie eine schnelle Entwicklung wünschen. Auf diese Weise haben Sie das frühe Optimierungssyndrom vermieden, und jetzt möchten Sie Engpassteile optimieren, großartig! Aber wenn Sie die C/Python-Schnittstelle von Hand machen, werden Sie mit Sicherheit das frühe Optimierungssyndrom finden.

Wenn Sie etwas mit weniger Schnittstellencode möchten, können Sie über das Erstellen einer DLL aus Ihrem C-Code nachdenken und diese Bibliothek direkt von Python mit cstruct verwenden.

Betrachten Sie auch Cython10, wenn Sie nur Python-Code in Ihrem Programm verwenden möchten.

+3

Sie haben Cython falsch eingegeben. Wenn Sie sich bewusst sind, wie Cython funktioniert, kann es ein sehr schneller Prozess sein, eine Python-Funktion in Cython zu konvertieren. Ich hatte 30x Beschleunigungen, die dies für nicht viel zusätzliche Anstrengung tun. –

+1

"Mit Sicherheit werden Sie immer einen Leistungszuwachs von Hand machen" - mit modernen Tools ist das höchst unwahrscheinlich.Die Chancen, dass Sie als Programmierer einen möglichst optimalen Code schreiben (mit möglichst wenig Aufwand und Zeitaufwand) als ein Tool, das von Tausenden von Menschen geschrieben und getestet wurde, sind ziemlich dünn. Bedenken Sie, dass ich Ihre Programmierkenntnisse nicht mit dieser Aussage beleidigen möchte. – rbaleksandar

3

Wenn es keine große Erweiterung ist, könnte boost :: python auch eine Option sein, es wird schneller ausgeführt als swig, weil Sie kontrollieren, was passiert, aber es dauert länger bis zum Entwickeln.

Der Overhead von swig ist immer dann akzeptabel, wenn der Arbeitsaufwand innerhalb eines einzelnen Anrufs groß genug ist. Wenn Sie zum Beispiel angeben, dass Sie einen mittelgroßen Logikblock haben, den Sie nach C/C++ verschieben möchten, aber dieser Block innerhalb einer engen Schleife aufgerufen wird, müssen Sie möglicherweise Schluck vermeiden, aber ich kann nicht wirklich denken von irgendwelchen realen Beispielen außer Skript-Grafik-Shadern.

57

Sie sollten Boost.Python in Betracht ziehen, wenn Sie nicht vorhaben, auch Bindungen für andere Sprachen mit swig zu generieren.

Wenn Sie viele Funktionen und Klassen zum Binden haben, ist Py++ ein großartiges Tool, das automatisch den benötigten Code generiert, um die Bindungen zu erstellen.

Pybindgen kann auch eine Option sein, aber es ist ein neues Projekt und weniger vollständig als Boost.Python.


Edit:

Vielleicht muss ich noch deutlicher über Vor- und Nachteile sein.

  • Swig:

    pro: Sie Bindungen für viele Skriptsprachen erzeugen.

    Nachteile: Ich mag nicht, wie der Parser funktioniert. Ich weiß nicht, ob das etwas Fortschritte gemacht hat, aber vor zwei Jahren war der C++ - Parser ziemlich begrenzt. Meistens musste ich meine .h-Header kopieren/einfügen, einige % Zeichen hinzufügen und dem swig-Parser zusätzliche Hinweise geben.

    Ich musste auch mit der Python C-API von Zeit zu Zeit für (nicht so) komplizierte Typkonvertierungen umgehen.

    Ich benutze es nicht mehr.

  • Boost.Python:

    pro: Es ist eine sehr komplette Bibliothek. Es ermöglicht Ihnen fast alles, was mit der C-API möglich ist, aber in C++. Ich musste nie C-API-Code mit dieser Bibliothek schreiben. Ich bin auch nie auf Fehler wegen der Bibliothek gestoßen. Code für Bindings funktioniert entweder wie Charm oder Compile.

    Dies ist wahrscheinlich eine der besten verfügbaren Lösungen, wenn Sie bereits eine C++ - Bibliothek zum Binden haben.Aber wenn Sie nur eine kleine C-Funktion zum Umschreiben haben, würde ich wahrscheinlich mit Cython versuchen.

    Nachteile: Wenn Sie keine vorkompilierte Boost.Python-Bibliothek haben, werden Sie Bjam (eine Art Ersatz) verwenden. Ich hasse Bjam und seine Syntax wirklich.

    Python-Bibliotheken, die mit B.P erstellt wurden, neigen dazu, fettleibig zu werden. Es dauert auch ein Los von Zeit, um sie zu kompilieren.

  • Py ++ (nicht fortgeführt): es ist Boost.Python leicht gemacht. Py ++ verwendet einen C++ - Parser, um Ihren Code zu lesen, und generiert dann automatisch Boost.Python-Code. Du hast auch eine großartige Unterstützung von seinem Autor (nein ich bin es nicht ;-)).

    Nachteile: nur die Probleme aufgrund Boost.Python selbst. Update: Ab 2014 sieht dieses Projekt nicht mehr fort.

  • Pybindgen:

    Er erzeugt den Code mit dem C-API handelt. Sie können entweder Funktionen und Klassen in einer Python-Datei beschreiben oder pybindgen Ihre Header lesen lassen und automatisch Bindings generieren (hierfür wird pygccxml verwendet, eine vom Autor von Py ++ geschriebene Python-Bibliothek).

    Nachteile: Es ist ein junges Projekt, mit einem kleineren Team als Boost.Python. Es gibt noch einige Einschränkungen: Sie können nicht mehrere Vererbungen für Ihre C++ - Klassen verwenden, Callbacks (nicht automatisch, jedoch kann auch ein Code für die benutzerdefinierte Rückrufbehandlung geschrieben werden). Übersetzung von Python-Ausnahmen zu C.

    Es ist definitiv einen guten Blick wert.

  • Ein neues: Am 2009/01/20 hat der Autor von Py ++ eine new package für die Schnittstelle von C/C++ - Code mit Python angekündigt. Es basiert auf Ctypes. Ich habe es nicht schon versucht, aber ich werde es tun! Hinweis: Dieses Projekt sieht nicht wie Py ++ aus.

  • CFFI: Ich kannte die Existenz dieser bis vor kurzem nicht, so kann ich jetzt meine Meinung nicht geben. Es sieht so aus, als könnten Sie C-Funktionen in Python-Strings definieren und sie direkt vom selben Python-Modul aus aufrufen.

  • Cython: Dies ist die Methode, die ich derzeit in meinen Projekten verwende. Grundsätzlich schreiben Sie Code in spezielle .pyx-Dateien. Diese Dateien werden in C-Code übersetzt (übersetzt), die ihrerseits in CPython-Module kompiliert werden. Cython-Code kann wie reguläres Python aussehen (und in der Tat sind reine Python-Dateien .pyx Cython-Dateien), aber Sie können auch mehr Informationen wie Variablentypen erhalten. Diese optionale Eingabe ermöglicht es Cython, schneller C-Code zu generieren. Code in Cython-Dateien kann sowohl reine Python-Funktionen als auch C- und C++ - Funktionen (und auch C++ - Methoden) aufrufen.

    Es dauerte einige Zeit in Cython zu denken, dass in der gleichen Code-Aufruf C und C++ Funktion, Python und C-Variablen mischen, und so weiter. Aber es ist eine sehr mächtige Sprache, mit einer aktiven (in 2014) und freundlichen Gemeinschaft.

+7

danke, swig hat sich in den letzten Jahren weiterentwickelt. Du gibst einfach deine .h Datei ein und alles ist erledigt (noch eine Stunde für Unicode-Unterstützung und du bist wirklich fertig :-) Mein Code funktioniert gut mit swig - meine Frage war, ob es sich lohnt, den generierten .py Code manuell zu durchlaufen mit allen pySwigObjects ... – RSabet

+1

Diese Objekte fügen einige Overhead hinzu. Pybindgen kann wahrscheinlich viel saubereren C-Code für Ihr Modul erzeugen. Ich habe ein bisschen damit gespielt und tatsächlich einen Code erzeugt, der sehr nahe an dem war, was ich manuell getan hätte. – ascobol

+0

Ich habe Ihre Py ++ - URL aktualisiert, die veraltet war. – JBentley

3

Bevor Sie Ihren Python-Code aufgeben, werfen Sie einen Blick auf ShedSkin. Sie behaupten eine bessere Leistung als Psyco auf irgendeinem Code (und erklären Sie auch, dass es noch experimentell ist).

Ansonsten gibt es mehrere Möglichkeiten, C/C++ - Code an Python zu binden.

Boost ist langwierig zu kompilieren, ist aber wirklich die flexibelste und benutzerfreundlichste Lösung.

Ich habe noch nie SWIG verwendet, aber im Vergleich zu Boost ist es nicht so flexibel wie sein generisches Binding Framework, kein Framework für Python. Die nächste Wahl ist Pyrex. Es ermöglicht das Schreiben von Pseudo-Python-Code, der als C-Erweiterung kompiliert wird.

+0

danke, aber diese können nicht mit einfachen C/C++ vergleichen. – RSabet

+0

Ich erwähne Boost, das ist reines C++, also ist Ihr Kommentar unangemessen. –

+0

Ich bezog mich auf PyRex. Boost wäre nur eine vielleicht flexiblere Alternative zum Schluck. Aber was würde ich gerne wissen, wenn es eine erhebliche Leistungssteigerung ist, wenn Hand schreiben die Schnittstelle, anstatt Schluck oder Boost den Code zu erstellen. Trotzdem danke für die Antwort. ShedSkin war neu für mich. – RSabet

16

Mit Cython ist ziemlich gut. Sie können Ihre C-Erweiterung mit einer Python-ähnlichen Syntax schreiben und C-Code generieren lassen. Kesselplatte enthalten. Da Sie den Code bereits in Python haben, müssen Sie nur ein paar Änderungen an Ihrem Engpasscode vornehmen und C-Code wird daraus generiert.

Beispiel. hello.pyx:

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

Die 601 Zeilen Codetextvorschlag erzeugt:

/* 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); 
    } 
} 
+0

Beachten Sie, dass Sie den C-Code auch separat schreiben und dann mit 'cython' verbinden können, indem Sie ihn in Python zuordnen. Zumindest habe ich es so benutzt (und laut den Docs ist es ein absolut legitimer Weg es zu benutzen). – rbaleksandar

4

Hier gibt es Drachen sein. Nicht schwingen, nicht aufladen. Für jedes komplizierte Projekt wird der Code, den Sie selbst ausfüllen müssen, damit er funktioniert, schnell unübersichtlich. Wenn es sich um eine einfache C-API für Ihre Bibliothek handelt (keine Klassen), können Sie einfach ctypes verwenden. Es wird einfach und schmerzlos sein, und Sie werden nicht stundenlang durch die Dokumentation dieser labyrinthischen Wrapper-Projekte gehen müssen, um die eine kleine Notiz über die Funktion zu finden, die Sie brauchen.

+7

Diese anderen Wrapper existieren, weil Ctypes nicht das ist, was jeder will, Sie wissen ... –

5

Da Sie mit Geschwindigkeit und Overhead betroffen sind, empfehle ich unter Berücksichtigung PyBindGen.

Ich habe Erfahrung damit, um eine große interne C++ - Bibliothek zu wickeln. Nach dem Versuch, SWIG, SIP, und Boost.Python Ich ziehe PyBindGen aus den folgenden Gründen:

  1. A PyBindGen Wrapper pure-Python ist, keine Notwendigkeit, ein anderes Dateiformat
  2. PyBindGen erzeugt Python-C-API zu lernen direkt aufruft, Es gibt keine geschwindigkeitsraubende Indirektionsschicht wie SWIG.
  3. Der generierte C-Code ist sauber und einfach zu verstehen. Ich mag auch Cython, aber der Versuch, seine C-Ausgabe zu lesen, kann manchmal schwierig sein.
  4. STL Sequenzcontainer werden unterstützt (wir viel std :: Vektors verwenden)
7

Eine Beobachtung: Basierend auf dem Benchmarking von dem pybindgen Entwickler durchgeführt, gibt es keinen signifikanten Unterschied zwischen boost.python und säuft. Ich habe kein eigenes Benchmarking durchgeführt, um zu überprüfen, wie viel davon von der richtigen Verwendung der boost.python-Funktionalität abhängt.

Beachten Sie auch, dass es einen Grund dafür, dass pybindgen im Allgemeinen ziemlich viel schneller zu sein scheint als swig und boost.python: es kann nicht so vielseitig wie die beiden anderen eine Bindung herzustellen. Zum Beispiel, Exception Propagation, Call Argument Type Checking, etc. Ich hatte noch keine Chance, pybindgen zu benutzen, aber ich beabsichtige es.

Boost ist in der Regel recht großes Paket zu installieren, und zuletzt sah ich, dass Sie Boost Python nicht einfach installieren können Sie brauchen die gesamte Boost-Bibliothek. Wie andere bereits erwähnt haben, wird die Kompilierung wegen der starken Verwendung der Template-Programmierung langsam sein, was auch typisch eher kryptische Fehlermeldungen zur Kompilierzeit bedeutet.

Zusammenfassung: gegeben, wie einfach SWIG zu installieren und zu verwenden ist, dass es anständige Bindung erzeugt, die robust und vielseitig ist, und dass eine Interface-Datei Ihre C++ DLL aus mehreren anderen Sprachen wie LUA, C# und Java verfügbar macht Ich würde es über boost.python bevorzugen. Aber wenn Sie nicht wirklich mehrsprachige Unterstützung benötigen, würde ich PyBindGen aufgrund seiner angeblichen Geschwindigkeit genau unter die Lupe nehmen und genau auf die Robustheit und Vielseitigkeit der von ihm erzeugten Bindung achten.

26

SWIG 2.0.4 hat eine neue Option -builtin eingeführt, die die Leistung verbessert. Ich habe ein Benchmarking mit einem Beispielprogramm durchgeführt, das viele schnelle Aufrufe an eine C++ Erweiterung ausführt. Ich baute die Erweiterung mit boost.python, PyBindGen, SIP und SWIG mit und ohne die Option -builtin. Hier sind die Ergebnisse (Durchschnitt von 100 Läufen):

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

SWIG verwendet, um am langsamsten zu sein. Mit der neuen Option -builtin scheint SWIG am schnellsten zu sein.

+2

Können Sie mehr darüber erweitern, besonders erwähnen Sie den Benchmark-Code, den Sie in C-APIs umgewandelt haben. –