当前位置: 首页 > news >正文

Pybind11的使用

目录

  • 1. 引言
    • 1.1 Pybind11 简介
    • 1.2 为什么需要 Pybind11
  • 2. 使用 Pybind11 进行 C++ 与 Python 交互
    • 2.1 基本用法
    • 2.2 编译与生成共享库
      • 2.2.1 在 Linux 下编译
      • 2.2.2 在 macOS 下编译
      • 2.2.3 编译选项详解
    • 2.3 在 Python 中使用编译后的模块
  • 3. 高级用法与注意事项
    • 3.1 绑定类和复杂数据结构
    • 3.2 与 NumPy 交互
    • 3.3 随机数生成器的兼容性问题
  • 4. 一些🌰
    • 4.1 高性能矩阵运算
    • 4.2 图像处理
    • 4.3 处理大规模数据
  • Ref

1. 引言

在现代软件开发中,Python 以其简洁的语法和丰富的生态系统,成为了数据科学和机器学习领域的首选语言。然而,Python 在执行速度方面并不总是能够满足高性能计算的需求。C++ 则以其卓越的性能和强大的系统级编程能力,常用于性能要求极高的场景。Pybind11 是一个开源工具,旨在简化 C++ 与 Python 之间的交互,使得开发者可以在 Python 中方便地调用 C++ 编写的高性能代码。

1.1 Pybind11 简介

Pybind11 是一个轻量级的头文件库,允许在 C++11 及以上标准下,将 C++ 的函数、类和数据结构绑定到 Python 中。它的设计目标是替代 Boost.Python,但没有后者的编译复杂性和体积。通过 Pybind11,开发者可以:

  • 高效地将 C++ 函数和类暴露给 Python:无需复杂的配置,即可在 Python 中调用 C++ 代码。
  • 支持现代 C++ 特性:兼容 C++11 及以上标准,支持智能指针、模板、枚举等特性。
  • 无缝集成:支持 numpy、Eigen 等常用库的数据类型转换。

1.2 为什么需要 Pybind11

在某些应用场景下,Python 的执行效率可能成为瓶颈,例如:

  1. 计算密集型任务:如大规模矩阵计算、图像处理、机器学习中的核心算法等。
  2. 实时系统:需要对性能有严格要求的系统,如高频交易、实时信号处理等。
  3. 遗留代码复用:已有大量用 C++ 编写的代码库,希望在 Python 项目中直接复用。

使用 Pybind11,可以将性能关键的部分用 C++ 编写,并通过简单的绑定在 Python 中调用,从而兼顾开发效率和运行效率。

2. 使用 Pybind11 进行 C++ 与 Python 交互

2.1 基本用法

Pybind11 的基本用法是通过编写 C++ 代码,将函数或类绑定到 Python 中。以下是一个简单的示例:

// example.cpp
#include <pybind11/pybind11.h>int add(int i, int j) {return i + j;
}PYBIND11_MODULE(example, m) {m.def("add", &add, "A function which adds two numbers");
}

在这个示例中,我们定义了一个简单的 add 函数,并使用 PYBIND11_MODULE 宏将其绑定到 Python 模块 example 中。

2.2 编译与生成共享库

为了在 Python 中使用我们编写的 C++ 模块,需要将其编译为共享库。不同的操作系统下,编译命令略有不同。

2.2.1 在 Linux 下编译

g++ -O3 -Wall -shared -std=c++11 -fPIC $(python3 -m pybind11 --includes) example.cpp -o example$(python3-config --extension-suffix)

命令解析:

  • g++:GNU C++ 编译器。
  • -O3:最高级别的优化,提升运行效率。
  • -Wall:开启所有警告,帮助发现潜在问题。
  • -shared:生成共享库(.so 文件)。
  • -std=c++11:使用 C++11 标准。
  • -fPIC:生成与位置无关的代码,适用于共享库。
  • $(python3 -m pybind11 --includes):获取 Pybind11 和 Python 的头文件路径。
  • example.cpp:源文件。
  • -o example$(python3-config --extension-suffix):指定输出文件名,$(python3-config --extension-suffix) 获取适当的文件扩展名(如 .so)。

2.2.2 在 macOS 下编译

g++ -O3 -Wall -shared -std=c++11 -undefined dynamic_lookup $(python3 -m pybind11 --includes) example.cpp -o example$(python3-config --extension-suffix)

命令解析:

  • 与 Linux 下基本相同,区别在于:
    • -undefined dynamic_lookup:macOS 特有选项,允许在链接时未定义的符号在运行时解析。

2.2.3 编译选项详解

  • -O3 优化级别

    -O3 是编译器提供的最高级别优化选项,启用所有优化以提升代码运行效率。然而,需要注意的是,过度优化可能会导致编译时间增加,甚至在某些情况下引入不稳定性。

  • -Wall 警告信息

    -Wall 选项开启了编译器的所有常用警告,这有助于在编译阶段发现潜在的代码问题,提高代码质量。

  • -shared 生成共享库

    共享库(Linux 下为 .so 文件,macOS 下为 .dylib.so)可以被多个程序同时使用,节省内存和磁盘空间。在 Python 中,扩展模块通常以共享库的形式存在。

  • -std=c++11 指定标准

    指定使用 C++11 标准,使得我们可以在代码中使用现代 C++ 特性,如智能指针、自动类型推导等。

  • -fPIC 位置无关代码

    生成的位置无关代码可以在内存中任意位置加载,适用于共享库的生成。

  • -undefined dynamic_lookup

    这是 macOS 下的特定选项,允许共享库在编译时不解析未定义的符号,而是在运行时解析。这对于动态语言的扩展模块非常有用。

2.3 在 Python 中使用编译后的模块

编译成功后,会生成一个共享库文件,例如 example.cpython-38-x86_64-linux-gnu.so。在 Python 中,我们可以直接导入并使用:

import exampleresult = example.add(3, 4)
print(result)  # 输出 7

3. 高级用法与注意事项

3.1 绑定类和复杂数据结构

除了函数,Pybind11 也支持将 C++ 类绑定到 Python 中。例如:

#include <pybind11/pybind11.h>class Pet {
public:Pet(const std::string &name) : name(name) {}void setName(const std::string &name_) { name = name_; }const std::string &getName() const { return name; }
private:std::string name;
};PYBIND11_MODULE(example, m) {pybind11::class_<Pet>(m, "Pet").def(pybind11::init<const std::string &>()).def("setName", &Pet::setName).def("getName", &Pet::getName);
}

在 Python 中:

import examplepet = example.Pet("Milo")
print(pet.getName())  # 输出 "Milo"
pet.setName("Otis")
print(pet.getName())  # 输出 "Otis"

3.2 与 NumPy 交互

Pybind11 提供了对 NumPy 数组的支持,可以方便地在 C++ 和 Python 之间传递数组。

#include <pybind11/pybind11.h>
#include <pybind11/numpy.h>double sum_array(pybind11::array_t<double> input) {auto buf = input.request();double *ptr = (double *) buf.ptr;size_t size = buf.size;double sum = 0;for (size_t idx = 0; idx < size; idx++)sum += ptr[idx];return sum;
}PYBIND11_MODULE(example, m) {m.def("sum_array", &sum_array, "Sum elements of a NumPy array");
}

在 Python 中:

import example
import numpy as nparr = np.array([1.0, 2.0, 3.0])
result = example.sum_array(arr)
print(result)  # 输出 6.0

3.3 随机数生成器的兼容性问题

在跨语言使用随机数生成器时,需要注意不同语言和库之间的兼容性问题。例如,C++11 提供了 std::mt19937 随机数生成器,而 NumPy 的 np.random.RandomState 则是 Python 中常用的随机数生成器。

问题描述:

  • np.random.RandomStatestd::mt19937 不兼容。这意味着,使用 Pybind11 将 C++ 的随机数生成器暴露给 Python 时,不能直接在 Python 中使用 np.random.RandomState 来设置 C++ 中生成器的状态。

解决方案:

  • 统一随机数生成器:在 C++ 和 Python 中使用相同的随机数生成算法和种子。
  • 通过种子传递:让 Python 将种子传递给 C++,在 C++ 中初始化 std::mt19937
  • 避免共享状态:将随机数的生成完全放在 C++ 或 Python 中,避免在两者之间共享生成器状态。
#include <pybind11/pybind11.h>
#include <random>double random_double(int seed) {std::mt19937 gen(seed);std::uniform_real_distribution<> dis(0.0, 1.0);return dis(gen);
}PYBIND11_MODULE(example, m) {m.def("random_double", &random_double, "Generate a random double");
}

在 Python 中:

import exampleseed = 42
value = example.random_double(seed)
print(value)

通过在 Python 中控制种子,可以在 C++ 中生成可重复的随机数。

4. 一些🌰

为了更深入地理解 Pybind11 的使用,我们将结合实际例子,探讨如何在项目中应用 Pybind11。

4.1 高性能矩阵运算

假设我们需要对大型矩阵进行复杂的运算,而 Python 的性能无法满足需求。我们可以使用 C++ 编写核心计算逻辑,并通过 Pybind11 暴露给 Python。

#include <pybind11/pybind11.h>
#include <pybind11/numpy.h>pybind11::array_t<double> matrix_add(pybind11::array_t<double> a, pybind11::array_t<double> b) {auto buf_a = a.request(), buf_b = b.request();if (buf_a.size != buf_b.size)throw std::runtime_error("Input shapes must match");double *ptr_a = (double *) buf_a.ptr;double *ptr_b = (double *) buf_b.ptr;pybind11::array_t<double> result(buf_a.size);auto buf_result = result.request();double *ptr_result = (double *) buf_result.ptr;for (size_t idx = 0; idx < buf_a.size; idx++)ptr_result[idx] = ptr_a[idx] + ptr_b[idx];result.resize({buf_a.shape[0], buf_a.shape[1]});return result;
}PYBIND11_MODULE(example, m) {m.def("matrix_add", &matrix_add, "Add two matrices");
}

在 Python 中:

import example
import numpy as npa = np.ones((1000, 1000))
b = np.ones((1000, 1000))result = example.matrix_add(a, b)
print(result)

通过这种方式,我们可以大幅提升矩阵运算的性能。

4.2 图像处理

在图像处理领域,性能也是关键因素。我们可以利用 C++ 的高性能和 OpenCV 等库,加速图像处理任务。

#include <pybind11/pybind11.h>
#include <pybind11/numpy.h>
#include <opencv2/opencv.hpp>pybind11::array_t<uint8_t> convert_to_grayscale(pybind11::array_t<uint8_t> input) {auto buf = input.request();cv::Mat img(buf.shape[0], buf.shape[1], CV_8UC3, (uint8_t *) buf.ptr);cv::Mat gray;cv::cvtColor(img, gray, cv::COLOR_BGR2GRAY);pybind11::array_t<uint8_t> result({buf.shape[0], buf.shape[1]}, gray.data);return result;
}PYBIND11_MODULE(example, m) {m.def("convert_to_grayscale", &convert_to_grayscale, "Convert image to grayscale");
}

在 Python 中:

import example
import cv2
import numpy as npimg = cv2.imread('image.jpg')
gray = example.convert_to_grayscale(img)
cv2.imwrite('gray_image.jpg', gray)

4.3 处理大规模数据

在数据科学中,处理大规模数据时,经常需要优化代码性能。以下是一个计算大数组元素平方和的示例:

#include <pybind11/pybind11.h>
#include <pybind11/numpy.h>double sum_of_squares(pybind11::array_t<double> input) {auto buf = input.request();double *ptr = (double *) buf.ptr;size_t size = buf.size;double sum = 0;for (size_t idx = 0; idx < size; idx++)sum += ptr[idx] * ptr[idx];return sum;
}PYBIND11_MODULE(example, m) {m.def("sum_of_squares", &sum_of_squares, "Compute sum of squares");
}

在 Python 中:

import example
import numpy as npdata = np.random.rand(1000000)
result = example.sum_of_squares(data)
print(result)

Ref

[1] https://pybind11.readthedocs.io/en/stable/compiling.html#building-manually

http://www.lryc.cn/news/440720.html

相关文章:

  • 鸿蒙-沉浸式pc端失效
  • 【资料分析】刷题日记1
  • nodejs+express+vue教辅课程辅助教学系统 43x2u前后端分离项目
  • 96-javahashmap底层原理
  • AI逻辑推理入门
  • 力扣3014.输入单词需要的最少按键次数I
  • 【Git】远程仓库
  • 苹果手机铃声怎么设置自己的歌?3个方法自定义手机铃声
  • 828华为云征文|华为Flexus云服务器搭建Cloudreve私人网盘
  • 【AI学习】AI绘画发展简史
  • 使用LangChain创建简单的语言模型应用程序【快速入门指南】
  • 嵌入式人工智能项目及人工智能应用项目——大合集列表查阅
  • 心觉:成功学就像一把刀,有什么作用关键在于使用者(一)
  • GAMES101(10节,几何)
  • Android 中音频焦点的使用场景及示例
  • 2. JDBC驱动是什么?如何在Java项目中配置MySQL的JDBC驱动?
  • Nginx 跨域 + 无法设置 Cookie 解决办法
  • 北森笔试测评之言语理解到底难不难
  • Ubuntu下beanstalkd无法绑定局域网IP地址以及消息队列beanstalkd上的error: JOB_TOO_BIG的解决
  • 网络原理(3)—— 应用层、传输层(TCP)
  • Flutter - Win32程序是如何执行main函数
  • linux-系统管理与监控-日志管理
  • VulhubDC-4靶机详解
  • [数据集][目标检测]烟叶病害检测数据集VOC+YOLO格式612张3类别
  • Sapiens——人类视觉大模型的基础
  • 《深度学习》【项目】 OpenCV 身份证号识别
  • 机器学习实战—天猫用户重复购买预测
  • 一款rust语言AI神器cursor在ubuntu环境下的安装启动教程
  • 【C#生态园】发现C#中的数据科学魔法:6款不可错过的库详解
  • 导入neo4j数据CSV文件及csv整理demo示例