Python与C/C++的交互

2020/02/12 Python C++ C

Pybind和Python C用法

C++不一定比Python运行快,在“起跑”阶段,C++甚至比Python要慢。我们使用C++主要是为了加速大段Python代码。

  • 因为Python调用C++需要进行参数转换和返回值转换,这个也会耗费时间。
  • Python的.会搜寻很多东西才能获得到对象的属性、方法等,也会影响执行速度。
  • 虽然Python调用C++在类型转换上会有速度损失,但是在进入到函数提内运行过程中的速度是不影响的,假如我们的运算量够大,完全可以弥补那一点点性能影响,所以要想重复利用C++的速度,尽量少调用C++,把计算结果竟然一次性返回,而不是我们多次进行交互,这样就能最大化利用C++。

如何让你的Python更快 pybind github pybind11 文档 Python3: Python C API参考 Python2: Python C API参考

GIL的获取和释放

当Python端调用C++端的代码时,如果不在C++端主动释放GIL锁,该线程会一直hold GIL锁。

  • Pybind用法:py::gil_scoped_release:释放GIL锁;py::gil_scoped_acquire:获取GIL锁
  • Python C用法:可以使用Py_BEGIN_ALLOW_THREADS和Py_END_ALLOW_THREADS这一对宏来释放GIL Py_BEGIN_ALLOW_THREADS / Py_END_ALLOW_THREADS ;使用gstate = PyGILState_Ensure()和PyGILState_Release(gstate)来获取GIL

GIL

核对当前C++程序是否占用GIL锁

Python之丢掉万恶的GIL

Python对象

参考

py::cast(返回转换后的py::object对象,是右值,不能对其做取地址操作)、obj.cast

Python types 包括handle, object, bool_, int_, float_, str, bytes, tuple, list, dict, slice, none, capsule, iterable, iterator, function, buffer, array, and array_t.

  • handle: Holds a reference to a Python object (no reference counting)
  • inc_ref(): increase the reference count of the Python object
  • dec_ref(): decrease the reference count of the Python object
  • object: Holds a reference to a Python object (with reference counting)
  • array: https://pybind11.readthedocs.io/en/stable/advanced/pycpp/numpy.html

ref_count(): Return the object’s current reference count.

Python调用C/C++函数

  • 返回值:返回值策略
  • 增加调用策略:call_guard<T>
  • 参数:Python object可以直接作为参数(例如上面Python对象中的dict等);*args和**kwargs也可以作为参数(py::args起源于py::tuple,py::kwargs起源于py::dict);通过py::arg定义默认参数

智能指针

std::unique_ptr(不能作为参数)、std::shared_ptr 参考

The binding generator for classes, class_, can be passed a template type that denotes a special holder type that is used to manage references to the object. If no such holder type template argument is given, the default for a type named Type is std::unique_ptr, which **means that the object is deallocated when Python’s reference count goes to zero**.

构造函数:

  • py::init 参考
  • 还有一种__init__函数。类似如下:
    py::class_<Raster>(m, "Raster", py::buffer_protocol())
      .def("__init__", [](Raster& raster, py::array_t<double> buffer, double spacingX, double spacingY, double spacingZ) {
      py::buffer_info info = buffer.request();
      new (&raster) Raster3D(static_cast<double*>(info.ptr), info.shape[0], info.shape[1], info.shape[2], spacingX, spacingY, spacingZ);
      })
    

参考

其他可以提速Python方法:

相关PR:

Search

    Table of Contents