找简历的网站,wordpress首页多重筛选,注册小程序要多少钱,注册企业注册公司目录 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 绑定类和复杂数… 目录 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 是一个轻量级的头文件库允许在 C11 及以上标准下将 C 的函数、类和数据结构绑定到 Python 中。它的设计目标是替代 Boost.Python但没有后者的编译复杂性和体积。通过 Pybind11开发者可以
高效地将 C 函数和类暴露给 Python无需复杂的配置即可在 Python 中调用 C 代码。支持现代 C 特性兼容 C11 及以上标准支持智能指针、模板、枚举等特性。无缝集成支持 numpy、Eigen 等常用库的数据类型转换。
1.2 为什么需要 Pybind11
在某些应用场景下Python 的执行效率可能成为瓶颈例如
计算密集型任务如大规模矩阵计算、图像处理、机器学习中的核心算法等。实时系统需要对性能有严格要求的系统如高频交易、实时信号处理等。遗留代码复用已有大量用 C 编写的代码库希望在 Python 项目中直接复用。
使用 Pybind11可以将性能关键的部分用 C 编写并通过简单的绑定在 Python 中调用从而兼顾开发效率和运行效率。
2. 使用 Pybind11 进行 C 与 Python 交互
2.1 基本用法
Pybind11 的基本用法是通过编写 C 代码将函数或类绑定到 Python 中。以下是一个简单的示例
// example.cpp
#include pybind11/pybind11.hint 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 -stdc11 -fPIC $(python3 -m pybind11 --includes) example.cpp -o example$(python3-config --extension-suffix)命令解析
gGNU C 编译器。-O3最高级别的优化提升运行效率。-Wall开启所有警告帮助发现潜在问题。-shared生成共享库.so 文件。-stdc11使用 C11 标准。-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 -stdc11 -undefined dynamic_lookup $(python3 -m pybind11 --includes) example.cpp -o example$(python3-config --extension-suffix)命令解析
与 Linux 下基本相同区别在于 -undefined dynamic_lookupmacOS 特有选项允许在链接时未定义的符号在运行时解析。
2.2.3 编译选项详解 -O3 优化级别 -O3 是编译器提供的最高级别优化选项启用所有优化以提升代码运行效率。然而需要注意的是过度优化可能会导致编译时间增加甚至在某些情况下引入不稳定性。 -Wall 警告信息 -Wall 选项开启了编译器的所有常用警告这有助于在编译阶段发现潜在的代码问题提高代码质量。 -shared 生成共享库 共享库Linux 下为 .so 文件macOS 下为 .dylib 或 .so可以被多个程序同时使用节省内存和磁盘空间。在 Python 中扩展模块通常以共享库的形式存在。 -stdc11 指定标准 指定使用 C11 标准使得我们可以在代码中使用现代 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) # 输出 73. 高级用法与注意事项
3.1 绑定类和复杂数据结构
除了函数Pybind11 也支持将 C 类绑定到 Python 中。例如
#include pybind11/pybind11.hclass 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::initconst 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()) # 输出 Otis3.2 与 NumPy 交互
Pybind11 提供了对 NumPy 数组的支持可以方便地在 C 和 Python 之间传递数组。
#include pybind11/pybind11.h
#include pybind11/numpy.hdouble sum_array(pybind11::array_tdouble 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.03.3 随机数生成器的兼容性问题
在跨语言使用随机数生成器时需要注意不同语言和库之间的兼容性问题。例如C11 提供了 std::mt19937 随机数生成器而 NumPy 的 np.random.RandomState 则是 Python 中常用的随机数生成器。
问题描述
np.random.RandomState 与 std::mt19937 不兼容。这意味着使用 Pybind11 将 C 的随机数生成器暴露给 Python 时不能直接在 Python 中使用 np.random.RandomState 来设置 C 中生成器的状态。
解决方案
统一随机数生成器在 C 和 Python 中使用相同的随机数生成算法和种子。通过种子传递让 Python 将种子传递给 C在 C 中初始化 std::mt19937。避免共享状态将随机数的生成完全放在 C 或 Python 中避免在两者之间共享生成器状态。
#include pybind11/pybind11.h
#include randomdouble 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.hpybind11::array_tdouble matrix_add(pybind11::array_tdouble a, pybind11::array_tdouble 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_tdouble 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.hpppybind11::array_tuint8_t convert_to_grayscale(pybind11::array_tuint8_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_tuint8_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.hdouble sum_of_squares(pybind11::array_tdouble 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