سؤال

لقد وجدت عنق الزجاجة في كود بايثون الخاص بي، ولعبت مع مريض نفسي وما إلى ذلك.ثم قررت كتابة امتداد c/c++ للأداء.

بمساعدة swig، لن تحتاج تقريبًا إلى الاهتمام بالحجج وما إلى ذلك.كل شيء يعمل بشكل جيد.

والآن سؤالي:يقوم swig بإنشاء ملف py كبير جدًا والذي يقوم بالكثير من "عمليات التحقق" و"PySwigObject" قبل استدعاء كود .pyd أو .so الفعلي.

هل لدى أي منكم أي خبرة فيما إذا كان هناك المزيد من الأداء الذي يمكنك الحصول عليه إذا كتبت هذا الملف يدويًا أو تركت swig يفعل ذلك.

هل كانت مفيدة؟

المحلول

وبالتأكيد سيكون لديك دائما كسب الأداء القيام بذلك من جهة، ولكن الربح يكون صغيرا جدا بالمقارنة مع الجهد المطلوب للقيام بذلك. ليس لدي أي شخصية لتعطيك ولكنني لا أنصح هذا، لأنك سوف تحتاج إلى الحفاظ على واجهة من جهة، وهذا ليس خيارا إذا وحدة بك كبيرة!

ويمكنك فعل الشيء الصحيح لاختيار لاستخدام لغة البرمجة النصية لأنك أردت التطور السريع. بهذه الطريقة كنت قد تجنب متلازمة الأمثل في وقت مبكر، والآن تريد تحسين أجزاء عنق الزجاجة، عظيم! ولكن إذا كنت تفعل واجهة C / الثعبان باليد سوف تقع في متلازمة الأمثل في وقت مبكر للتأكد.

إذا كنت تريد شيئا مع رمز أقل واجهة، يمكنك التفكير في خلق دلل من التعليمات البرمجية C الخاص بك، واستخدام تلك المكتبة مباشرة من الثعبان مع <لأ href = "http://python.net/crew/theller/ctypes / "يختلط =" noreferrer "> cstruct .

Cython إذا كنت ترغب في استخدام رمز الثعبان الوحيد في البرنامج.

نصائح أخرى

يجب أن تفكر في Boost.Python إذا كنت لا تخطط لإنشاء روابط للغات أخرى أيضًا باستخدام swig.

إذا كان لديك الكثير من الوظائف والفئات التي تريد ربطها، باي++ هي أداة رائعة تقوم تلقائيًا بإنشاء الكود المطلوب لإجراء الارتباطات.

بيبيندجن قد يكون أيضًا خيارًا، ولكنه مشروع جديد وأقل اكتمالاً من Boost.Python.


يحرر:

ربما أحتاج إلى أن أكون أكثر وضوحًا بشأن الإيجابيات والسلبيات.

  • جرعة كبيرة:

    طليعة:يمكنك إنشاء روابط للعديد من لغات البرمجة النصية.

    سلبيات:لا أحب الطريقة التي يعمل بها المحلل اللغوي.لا أعرف ما إذا كان قد تم إحراز بعض التقدم ولكن قبل عامين كان المحلل اللغوي لـ C++ محدودًا للغاية.في معظم الأوقات اضطررت إلى نسخ/لصق رؤوس .h الخاصة بي، مما يضيف بعضًا منها % الشخصيات وإعطاء تلميحات إضافية لمحلل swig.

    كنت بحاجة أيضًا للتعامل مع Python C-API من وقت لآخر لتحويلات النوع المعقدة (ليست كذلك).

    أنا لا أستخدمه بعد الآن.

  • دفعة بايثون:

    طليعة:إنها مكتبة كاملة جدًا.فهو يتيح لك القيام بكل ما هو ممكن تقريبًا باستخدام C-API، ولكن في C++.لم أضطر مطلقًا إلى كتابة كود C-API مع هذه المكتبة.كما أنني لم أواجه أي خطأ بسبب المكتبة.يعمل رمز الارتباطات إما مثل السحر أو يرفض التجميع.

    من المحتمل أن يكون أحد أفضل الحلول المتاحة حاليًا إذا كان لديك بالفعل بعض مكتبات C++ لربطها.ولكن إذا كان لديك فقط وظيفة C صغيرة لإعادة كتابتها، فمن المحتمل أن أحاول استخدام Cython.

    سلبيات:إذا لم يكن لديك مكتبة Boost.Python المترجمة مسبقًا، فسوف تستخدم Bjam (نوع من الاستبدال).أنا حقا أكره Bjam وبناء الجملة.

    تميل مكتبات بايثون التي تم إنشاؤها باستخدام B.P إلى الإصابة بالسمنة.كما يستغرق أ كثير من الوقت لتجميعها.

  • Py++ (توقف):لقد أصبح Boost.Python سهلاً.يستخدم Py++ محلل C++ لقراءة التعليمات البرمجية الخاصة بك ثم يقوم بإنشاء كود Boost.Python تلقائيًا.لديك أيضًا دعم كبير من مؤلفها (لا، ليس أنا؛-)).

    سلبيات:فقط المشاكل بسبب Boost.Python نفسها.تحديث:اعتبارًا من عام 2014، يبدو هذا المشروع متوقفًا الآن.

  • بيبيندجن:

    يقوم بإنشاء الكود الذي يتعامل مع C-API.يمكنك إما وصف الوظائف والفئات في ملف بايثون، أو السماح لـ Pybindgen بقراءة رؤوسك وإنشاء الروابط تلقائيًا (لهذا يستخدم pygccxml، وهي مكتبة بايثون كتبها مؤلف Py++).

    سلبيات:إنه مشروع شاب، مع فريق أصغر من فريق Boost.Python.لا تزال هناك بعض القيود:لا يمكنك استخدام الوراثة المتعددة لفئات C++ الخاصة بك، وعمليات الاسترجاعات (ليس تلقائيًا، ولكن يمكن كتابة كود معالجة رد الاتصال المخصص).ترجمة استثناءات بايثون إلى C.

    إنه بالتأكيد يستحق نظرة جيدة.

  • واحد جديد:في 2009/01/20 أعلن مؤلف Py++ عن أ حزمة جديدة لربط كود C/C++ مع بايثون.لأنه يقوم على ctypes.لم أحاول ذلك بالفعل لكنني سأفعل!ملحوظة:يبدو هذا المشروع متوقفًا، مثل Py++.

  • CFFI:لم أكن أعلم بوجود هذا الأمر حتى وقت قريب جدًا، لذا لا أستطيع الآن أن أعطي رأيي.يبدو أنه يمكنك تحديد وظائف C في سلاسل Python واستدعائها مباشرة من نفس وحدة Python.

  • سايثون:هذه هي الطريقة التي أستخدمها حاليًا في مشاريعي.تقوم بشكل أساسي بكتابة التعليمات البرمجية في ملفات .pyx خاصة.يتم تجميع هذه الملفات (ترجمتها) إلى كود C والتي بدورها يتم تجميعها إلى وحدات CPython.يمكن أن تبدو تعليمات برمجية Cython مثل لغة Python العادية (وفي الواقع تعد لغة Python النقية ملفات .pyx Cython صالحة)، ولكن يمكنك أيضًا الحصول على مزيد من المعلومات مثل أنواع المتغيرات.تسمح هذه الكتابة الاختيارية لـ Cython بإنشاء كود C أسرع.يمكن أن تستدعي التعليمات البرمجية الموجودة في ملفات Cython كلاً من وظائف Python النقية وأيضًا وظائف C وC++ (وكذلك طرق C++).

    لقد استغرق الأمر بعض الوقت للتفكير في Cython، أنه في نفس الكود يستدعي وظيفة C وC++، ويمزج متغيرات Python وC، وما إلى ذلك.لكنها لغة قوية للغاية، مع مجتمع نشط (في عام 2014) وودود.

قدم SWIG 2.0.4 خيارًا جديدًا مدمجًا يعمل على تحسين الأداء.لقد أجريت بعض الاختبارات المرجعية باستخدام برنامج نموذجي يقوم بالكثير من الاستدعاءات السريعة لامتداد 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 هو الأسرع.

استخدام سايثون إنه جيد جدا.يمكنك كتابة ملحق C الخاص بك باستخدام بناء جملة يشبه Python وجعله يقوم بإنشاء كود C.وشملت لوحة معيارية.نظرًا لأن لديك الكود بالفعل في بايثون، فما عليك سوى إجراء بعض التغييرات على كود عنق الزجاجة الخاص بك وسيتم إنشاء كود 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);
    }
}

وهناك ملاحظة: استنادا إلى المقارنة التي أجريت من قبل المطورين pybindgen، لا يوجد فرق كبير بين boost.python وجرعة كبيرة. أنا لم أفعل القياس الخاصة للتحقق من مدى هذا يعتمد على الاستخدام السليم وظائف boost.python.

ملحوظة أيضا أنه قد يكون هناك سبب أن pybindgen يبدو أن في عام أسرع قليلا جدا من اكرع وboost.python: أنه قد لا تنتج كما تنوعا ملزم كما الأخريين. على سبيل المثال، نشر استثناء، حجة دعوة نوع التحقق، وما إلى ذلك لم تتح لي فرصة لاستخدام pybindgen بعد ولكن أنوي.

والدافع هو بشكل عام حزمة كبيرة جدا لتثبيت، وأخيرا رأيت لا يمكنك فقط تثبيت دفعة الثعبان كنت الى حد كبير في حاجة إلى مكتبة دفعة كاملة. كما ذكر آخرون تجميع سيكون بطيئا بسبب الاستخدام المكثف للبرمجة قالب، وهو ما يعني أيضا رسائل الخطأ عادة خفي وليس في وقت الترجمة.

وملخص: نظرا لمدى إكرع هو سهل التركيب والاستخدام، وأنه يولد لائق ملزم هو قوي وتنوعا، وهذا الملف واجهة واحدة يسمح الخاص بك DLL C ++ لتكون متوفرة من عدة لغات أخرى مثل LUA، C #، وجافا، وأود أن تفضل على مدى boost.python. ولكن إلا إذا كنت حقا بحاجة إلى دعم متعدد اللغات وأود أن نلقي نظرة فاحصة على PyBindGen بسبب سرعتها المزعومة، وإيلاء اهتمام وثيق لمتانة وبراعة ملزم التي يولدها.

نظرًا لأنك مهتم بالسرعة والنفقات العامة، أقترح عليك التفكير في ذلك بايبيندجين .

لدي خبرة في استخدامه لتغليف مكتبة C++ داخلية كبيرة.بعد تجربة SWIG وSIP وBoost.Python أفضّل PyBindGen للأسباب التالية:

  1. غلاف PyBindGen هو لغة Python خالصة، ولا حاجة لتعلم تنسيق ملف آخر
  2. يقوم PyBindGen بإنشاء مكالمات Python C API مباشرة، ولا توجد طبقة غير مباشرة تسرق السرعة مثل SWIG.
  3. رمز C الذي تم إنشاؤه نظيف وسهل الفهم.أنا أحب Cython أيضًا، لكن محاولة قراءة مخرجات لغة C قد تكون صعبة في بعض الأحيان.
  4. يتم دعم حاويات تسلسل STL (نستخدم الكثير من std::vector's)

ويكون هناك التنين هنا. لا جرعة كبيرة، لا زيادة. للحصول على أي مشروع معقد رمز لديك لملء في نفسك لجعلها العمل يصبح لا يمكن السيطرة عليها بسرعة. لو كان C API عادي إلى المكتبة الخاصة بك (لا توجد فصول دراسية)، يمكنك فقط استخدام ctypes. وسوف تكون سهلة وغير مؤلمة، وأنك لن تضطر لقضاء ساعات الصيد بشباك الجر من خلال وثائق هذه المشاريع المجمع متاهة محاولة للعثور على مذكرة واحدة صغيرة حول ميزة التي تحتاج إليها.

إذا ليس لها امتداد كبير، وتعزيز :: الثعبان قد يكون أيضا خيار، يتم تنفيذها بشكل أسرع من جرعة كبيرة، لأنك السيطرة على ما يحدث، لكنه سوف يستغرق وقتا أطول لديف.

وعلى أي حال النفقات العامة جرعة كبيرة غير مقبول إذا كان حجم العمل في مكالمة واحدة كبيرة بما فيه الكفاية. على سبيل المثال إذا كنت لإصدار هو أن لديك بعض كتلة المنطق متوسطة الحجم التي تريد نقلها إلى C / C ++، ولكن تسمى تلك الكتلة ضمن حلقة ضيقة، في كثير من الأحيان، قد يكون لديك لتجنب جرعة كبيرة، ولكن لا أستطيع أن أفكر حقا أي في العالم الحقيقي أمثلة باستثناء تظليل الرسومات كتابتها.

وقبل التخلي عن كود الثعبان الخاص بك، إلقاء نظرة على ShedSkin . وهم يدعون أداء أفضل من Psyco على بعض رمز (وكذلك القول أنه لا يزال تجريبيا).

وعدا ذلك، هناك العديد من الخيارات للربط كود C / C ++ إلى الثعبان.

وBoost غير مطول لتجميع ولكن في الواقع أكثر مرنة وسهلة لاستخدام الحل.

ولقد استخدمت أبدا إكرع ولكن بالمقارنة مع زيادة، انها ليست مرنة كما أنها إطار ملزم عام، وليس إطارا مخصصة لبيثون.

واختيار التالي هو بيركس . انها تسمح لكتابة رمز الثعبان الزائفة التي يحصل جمعت كامتداد C.

هناك مقال يستحق القراءة حول هذا الموضوع Cython، pybind11، cffi - ما هي الأداة التي يجب أن تختارها؟

خلاصة سريعة لمن نفد صبره:

  • سايثون يجمع لغة بايثون الخاصة بك إلى C/C++ مما يسمح لك بتضمين C/C++ في كود بايثون.يستخدم الربط الثابت.لمبرمجي بايثون.

  • pybind11 (وboost.python) هو العكس.قم بربط الأشياء الخاصة بك في وقت الترجمة من جانب C++.لمبرمجي C++.

  • CFFI يسمح لك بربط العناصر الأصلية ديناميكيًا في وقت التشغيل.سهلة الاستخدام، ولكن عقوبة الأداء أعلى.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top