2009-01-19 49 views
58

我發現了Python代碼中的瓶頸,與psycho等玩過。然後決定編寫一個c/C++擴展以提高性能。將python擴展爲swig,而不是swig或Cython

在swig的幫助下,你幾乎不需要關心參數等。一切正常。

現在我的問題:在調用實際的.pyd或.so代碼之前,swig會創建一個非常大的py文件,它會執行大量的「檢查」和「PySwigObject」。

有沒有人有任何經驗,如果你手寫這個文件或讓swig做它,是否有更多的性能獲得。

回答

24

當然,你將永遠有這樣做的性能增益手,但收益將是非常小的工作要做到這一點。我沒有任何數字給你,但我不建議這樣做,因爲你需要手工維護接口,如果你的模塊很大,這不是一個選項!

您選擇使用腳本語言是正確的,因爲您希望快速開發。這樣你就避免了早期的優化綜合症,現在你想優化瓶頸部分,太好了!但是,如果你手工操作C/python接口,肯定會陷入早期的優化綜合症。

如果你想要更少的接口代碼的東西,你可以考慮從你的C代碼創建一個DLL,並直接使用該庫從Python與cstruct

如果您只想在程序中使用python代碼,請考慮Cython

3

如果它不是一個很大的擴展名,boost :: python也可能是一個選項,它執行的速度比swig快,因爲你可以控制發生的事情,但開發需要更長的時間。

如果單次調用的工作量足夠大,則可以接受swig的開銷。例如,如果你問的是你有一些中等大小的邏輯塊,你想轉移到C/C++,但是這個塊在一個緊密循環中被調用,經常你可能不得不避免swig,但我真的不能想到除了腳本化的圖形着色器之外的任何實際示例。

57

如果您不打算爲swig生成其他語言的綁定,則應該考慮Boost.Python。

如果您有很多要綁定的函數和類,Py++是一個很好的工具,可以自動生成所需的代碼來創建綁定。

Pybindgen也可能是一個選項,但是它是一個新項目,並且Boost.Python不太完整。


編輯:

也許我需要更加明確的利弊。

  • 痛飲:

    親:您可以生成許多腳本語言綁定。

    缺點:我不喜歡解析器的工作方式。我不知道是否取得了一些進展,但兩年前C++解析器非常有限。大多數情況下,我不得不復制/經過我的.h頭添加一些%字符,並給swig解析器提供額外的提示。

    我還需要不時地處理Python C-API(不是那麼)複雜的類型轉換。

    我不再使用它了。

  • Boost.Python的:

    親: 這是一個非常完整的圖書館。它允許你用C-API來做幾乎所有可能的事情,但是在C++中。我從來不必用這個庫編寫C-API代碼。我也從來沒有遇到由於圖書館的錯誤。綁定代碼或者像魅力或拒絕編譯。

    如果您已經有一些C++庫綁定,它可能是當前可用的最佳解決方案之一。但是如果你只有一個小的C函數來重寫,我可能會嘗試使用Cython。

    缺點:如果您沒有預編譯的Boost.Python庫,那麼您將使用Bjam(類型替換)。我非常討厭Bjam及其語法。

    用B.P創建的Python庫往往變得肥胖。它也需要批次來編譯它們。

  • Py ++(停產):它的Boost.Python變得簡單。 Py ++使用C++解析器來讀取您的代碼,然後自動生成Boost.Python代碼。你也有作者的大力支持(不,不是我;-))。

    缺點:只是由於Boost.Python本身的問題。更新:截至2014年,這個項目現在看起來停止了。

  • Pybindgen:

    它生成處理的C-API的代碼。您可以在Python文件中描述函數和類,或者讓Pybindgen自動讀取您的頭文件並自動生成綁定(爲此,它使用Pygccxml,Py ++作者編寫的python庫)。

    缺點:這是一個年輕的項目,擁有比Boost.Python更小的團隊。還是有一些限制:你不能爲你的C++類使用多重繼承,回調(不是自動的,但是可以編寫自定義的回調處理代碼)。將Python異常轉換爲C.

    這絕對值得一看。

  • 一個新的: 在2009/01/20上,Py ++的作者宣佈了一個new package用於連接C/C++代碼和python。它基於ctypes。我沒有嘗試過,但我會!注意:這個項目看起來令人不滿,如Py ++。

  • CFFI:直到最近我才知道這個存在,所以現在我不能給出我的意見。看起來您可以在Python字符串中定義C函數,並直接從相同的Python模塊調用它們。

  • Cython:這是我目前在我的項目中使用的方法。基本上你可以在特殊的.pyx文件中編寫代碼。這些文件被編譯(翻譯)成C代碼,然後C代碼被編譯成CPython模塊。 Cython代碼看起來像普通的Python(實際上純Python是有效的.pyx Cython文件),但是你也可以獲得更多的信息,比如變量類型。這種可選的打字方式允許Cython生成更快的C代碼。 Cython文件中的代碼既可以調用純Python函數,也可以調用C和C++函數(以及C++方法)。

    我花了一些時間思考在Cython中,在相同的代碼中調用C和C++函數,混合使用Python和C變量,等等。但它是一種非常強大的語言,擁有活躍的(2014年)和友好的社區。

+7

謝謝,swig在過去的幾年裏已經有了進展。你只是%包括你的.h文件,一切都完成了(另一個小時的Unicode支持,你真的完成了:-) 我的代碼工作正常與swig - 我的問題是,如果它是值得手動去通過生成的.py代碼與所有pySwigObjects ... – RSabet 2009-01-20 19:02:03

+1

這些對象增加了一些開銷。 Pybindgen可能會爲您的模塊生成更清潔的C代碼。我玩了一點,實際上它生成了一個非常接近我手動完成的代碼。 – ascobol 2009-01-21 08:35:11

+0

我已經更新過時的Py ++網址。 – JBentley 2014-04-11 21:13:53

3

放棄你的python代碼之前,看看ShedSkin。他們聲稱在某些代碼上性能優於Psyco(並且聲明它仍然是實驗性的)。

否則,將C/C++代碼綁定到python有幾種選擇。

Boost編譯時間很長,但卻是最靈活和最易於使用的解決方案。

我從來沒有使用過SWIG,但與boost相比,它不像它的通用綁定框架那麼靈活,而不是專用於python的框架。

接下來的選擇是Pyrex。它允許編寫作爲C擴展編譯的僞python代碼。

+0

謝謝,但那些無法與普通的C/C++比較。 – RSabet 2009-01-19 21:47:09

+0

我提到boost,這是純C++,所以你的評論是不恰當的。 – 2009-01-20 08:56:10

+0

我指的是PyRex。 Boost只是一個可能更靈活的替代swig的選擇。但是我想知道,在手寫界面時是否會有顯着的性能增益,而不是讓Swig或Boost創建代碼。還是謝謝你的答案。 ShedSkin對我來說是新的。 – RSabet 2009-01-23 18:04:31

16

使用Cython是相當不錯的。您可以使用類Python語法編寫C擴展,並讓它生成C代碼。包括鍋板。由於你的代碼已經在python中,所以你必須對你的瓶頸代碼做一些修改,並從中生成C代碼。

例子。 hello.pyx

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

即生成601行的樣板代碼

這裏
/* 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); 
    } 
} 
4

有是龍。不要嘔吐,不要提高。對於任何複雜的項目,您必須填寫自己的代碼以使其工作變得難以管理。如果它是一個簡單的C API到你的庫(沒有類),你可以使用ctypes。這將是容易和無痛的,你將不必花費數小時瀏覽這些迷宮般的包裝項目的文檔,試圖找到關於你需要的功能的一個小注釋。

5

既然你關心速度和開銷,我建議考慮PyBindGen

我有使用它來包裝大型內部C++庫的經驗。想痛飲,SIP和Boost.Python的之後,我更喜歡PyBindGen,原因如下:

  1. 一個PyBindGen包裝是純Python,無需學習另一種文件格式
  2. PyBindGen產生Python的C API直接調用,沒有像SWIG這樣的速度掠奪間接層。
  3. 生成的C代碼乾淨簡單易懂。我也喜歡Cython,但嘗試閱讀它的C輸出可能有時很困難。
  4. STL序列容器的支持(我們使用了很多的std ::的矢量的)
7

的觀察:根據由pybindgen開發商進行的基準測試,還有的Boost.Python和痛飲之間沒有顯著差異。我沒有做我自己的基準測試來驗證這些依賴於正確使用boost.python功能的程度。

還請注意,pybindgen似乎總體上比swig和boost.python快得多:它可能不會產生與其他兩種綁定一樣多功能的綁定。例如,異常傳播,調用參數類型檢查等。我還沒有機會使用pybindgen,但我打算。

Boost是一般非常大的安裝包,最後我看到你不能只安裝boost python你幾乎需要整個Boost庫。正如其他人提到的那樣,由於大量使用模板編程,編譯將會很慢,這也意味着編譯時通常會出現相當模糊的錯誤消息。

摘要:鑑於SWIG安裝和使用的方便性,它生成的體面綁定是健壯的和多功能的,並且一個接口文件允許您的C++ DLL可以從多種其他語言中獲得,如LUA,C#和Java ,我會贊成boost.python。但除非你真的需要多語言支持,否則我會仔細研究PyBindGen,因爲它的速度很快,並且要密切注意它所產生的綁定的穩健性和多功能性。

26

SWIG 2.0.4引入了一個新的-builtin選項來提高性能。 我做了一些基準測試,使用了一個快速調用C++擴展的示例程序。 我使用boost.python,PyBindGen,SIP和SWIG構建了擴展,帶和不帶-builtin選項。下面是結果(100次運行的平均值):

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

SWIG過去是最慢的。有了新的選擇,SWIG似乎是最快的。