Vitis AI 进阶认知(pybind11)

目录

1. 简介

2. 代码分析

2.1 pybind11 介绍

2.2 writefile 魔法命令

2.3 快速编译和加载

2.3.1 语法主体

2.3.2 查看模块位置

2.3.3 中间文件

2.4 编译器链接器标志

3. pybind11 语法

3.1 基础示例

3.1.1 example.cpp

3.1.2 编译

3.1.3 关键字参数

3.1.4 帮助信息

3.2 类的绑定

3.2.1 example.cpp

3.2.2 绑定 lambda 函数

3.2.3 帮助信息

3.3 vart::Runner 类绑定

4. 总结


1. 简介

  • pybind11 介绍
  • Jupyter Lab 中的快速编译和加载
  • 使用编译器链接器标志
  • pybind11 的基本用法
  • pybind11 的类的绑定用法
  • vart::Runner 类的 Python 绑定源代码解释

2. 代码分析

2.1 pybind11 介绍

from pynq.lib import pybind11

pybind11 是一个轻量级的、非侵入性的库,用于在 C++ 和 Python 之间创建绑定,即使用 Python 调用 C++ 代码,或者反过来,使用 C++ 调用 Python 代码。它允许开发者将计算密集部分用 C++ 实现,同时保持 Python 的易用性和灵活性。 

pybind11 的主要特点包括:

  • 易用性:它提供了一种简洁的方式来定义 Python 绑定。通常只需要少量的代码就可以将 C++ 功能暴露给 Python。
  • 无需额外依赖:pybind11 是一个 header-only 库,这意味着你不需要预先安装或构建库,只需包含相应的头文件即可。
  • 自动类型转换:它支持自动类型转换,可以自动处理 C++ 和 Python 类型之间的转换,如 STL 容器(如 std::vector、std::map 等)、智能指针等。
  • 扩展性:pybind11 支持继承、虚函数、重载函数等高级 C++ 特性。
  • 最小的运行时开销:由于其设计高效,使用 pybind11 创建的绑定具有很低的运行时开销。

在 PYNQ (Python Productivity for Zynq) 或类似环境中,pybind11 可以用来将 FPGA 或其他硬件加速功能与 Python 接口连接,实现高效的数据处理和硬件控制。

2.2 writefile 魔法命令

%%writefile common.h
#define HELLO "Hello world!\r\n"

%%writefile 是一个在 Jupyter Notebook 环境中使用的魔法命令(magic command)。它用于将单元格的内容写入到一个指定的文件中。这通常用于在 Jupyter Notebook 中快速创建脚本或其他文件,而无需离开 Notebook 界面。 

2.3 快速编译和加载

2.3.1 语法主体

%%pybind11 myModule;
#include <iostream>
#include "common.h"

void hello() {std::cout << HELLO << std::endl;}

int add(int a, int b) {
    return a + b;
}

int sub(int a, int b) {
    return a - b;
}

在大多数标准的 Jupyter 环境中,没有内置的支持直接编译和链接 C++ 代码的功能,尤其是涉及到如 pybind11 这类库的情况。通常,设计者需要在你的系统中独立编译这样的代码,然后在 Python 中导入生成的模块。

2.3.2 查看模块位置

import myModule
import inspect
import os

module_location = os.path.dirname(inspect.getfile(myModule))
print(module_location)
---
/root/jupyter_notebooks/dong/pybind

!ls -l
---
common.h
myModule.cpython-310-aarch64-linux-gnu.so
pybind.ipynb

2.3.3 中间文件

-rw-r--r-- 1 root root    535 Mar 11 01:34 myModule.cpp
-rw-r--r-- 1 root root    158 Mar 11 01:34 myModule.hpp
-rw-r--r-- 1 root root    179 Mar 11 01:34 temp.c

运行 %%pybind11 单元格时,会生成以上三个中间文件,其中 myModule.cpp 这个文件是最终用于编译和构建的源文件:

#include <pybind11/pybind11.h>
#include <iostream>
#include "common.h"

namespace py = pybind11;

void hello() {
    std::cout << HELLO << std::endl;
}

int add(int a, int b) {
    return a + b;
}

int sub(int a, int b) {
    return a - b;
}

int main() {
    return 0;
}

PYBIND11_MODULE(myModule, m) {
    m.doc() = "Pybind11 module myModule";
    m.def("hello", &hello, "A function with name hello");
    m.def("add", &add, py::arg("a"), py::arg("b"), "A function with name add");
    m.def("sub", &sub, py::arg("a"), py::arg("b"), "A function with name sub");
}

2.4 编译器链接器标志

cflags = "-O2 -fno-inline -std=c++17 -L/usr/lib -Wl,-rpath=/usr/lib -I/usr/include/opencv4/opencv -I/usr/include/opencv4 " + \
         "-I/usr/include/python3.10 -I/usr/include/python3.10 -fPIC -shared "
ldflags = "-lvart-runner -lopencv_videoio -lopencv_imgcodecs " + \
    "-lopencv_highgui -lopencv_imgproc -lopencv_core -lglog " + \
    "-lxir -lunilog -lpthread -L/usr/lib/python3.10/config-3.10-aarch64-linux-gnu -L/usr/lib -lpython3.10 "

%%pybind11 myModule;{cflags};{ldflags}
#include <iostream>
#include "common.h"

cflags

  • -O2: 优化代码以提高执行速度。
  • -fno-inline: 禁用内联函数。
  • -std=c++17: 使用C++17标准。
  • -L/usr/lib: 指定库文件的搜索路径。
  • -Wl,-rpath=/usr/lib: 设置运行时库路径。
  • -I/usr/include/opencv4/opencv: 包含OpenCV头文件的路径。
  • -I/usr/include/python3.10: 包含Python 3.10头文件的路径。
  • -fPIC: 生成位置无关代码(Position Independent Code)。
  • -shared: 生成共享库。

ldflags

  • -lvart-runner: 链接vart-runner库。
  • -lopencv_videoio: 链接OpenCV视频输入输出库。
  • -lopencv_imgcodecs: 链接OpenCV图像编解码库。
  • -lopencv_highgui: 链接OpenCV高层GUI库。
  • -lopencv_imgproc: 链接OpenCV图像处理库。
  • -lopencv_core: 链接OpenCV核心库。
  • -lglog: 链接Google日志库。
  • -lxir: 链接XIR库。
  • -lunilog: 链接UniLog库。
  • -lpthread: 链接POSIX线程库。
  • -L/usr/lib/python3.10/config-3.10-aarch64-linux-gnu: 指定Python 3.10配置库的路径。
  • -L/usr/lib: 指定库文件的搜索路径。
  • -lpython3.10: 链接Python 3.10库。

3. pybind11 语法

3.1 基础示例

3.1.1 example.cpp

#include <pybind11/pybind11.h>

int add(int a, int b) {
    return a + b;
}

PYBIND11_MODULE(example, m) {
    m.doc() = "pybind11 example by Dong"; // optional module docstring

    m.def("add", &add, "Dong's add function");
}

PYBIND11_MODULE() 宏创建一个函数,当从 Python 中发出 import 语句时将调用该函数。

  • 宏参数一:example,作为模块名称给出,且不应包含在引号中
  • 宏参数二: m, 定义 py::module_ 类型的变量,它是创建绑定的主接口。
  • 方法一:module_::doc(),模块文档字符串。
  • 方法二:module_::def(),生成向 Python 公开 add() 函数的绑定代码。

3.1.2 编译

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

此命令具体做了以下事情:

  • -O3 开启编译器的高级优化。
  • -Wall 开启所有警告。
  • -shared 生成共享库。
  • -std=c++11 指定使用 C++11 标准。
  • -fPIC 生成位置无关代码。
  • $(python3 -m pybind11 --includes) 插入 Pybind11 提供的必要编译器标志(例如头文件路径)。
  • example.cpp 是要编译的源文件。
  • -o example$(python3-config --extension-suffix) 指定输出文件名,扩展名由 python3-config --extension-suffix 提供,这通常是针对特定 Python 版本的动态库扩展名。

3.1.3 关键字参数

namespace py = pybind11;

m.def("add", &add, "A function which adds two numbers",
      py::arg("i"), py::arg("j"));

arg 是几个特殊标记类之一,可用于将元数据传递到 module_::def() 中。通过此修改后的绑定代码,可以使用关键字参数调用该函数,更具可读性,特别是对于采用多个参数的函数:

example.add(i=1, j=2)

3.1.4 帮助信息

help(example)
---
Help on module example:

NAME
    example - pybind11 example by Dong

FUNCTIONS
    add(...) method of builtins.PyCapsule instance
        add(a: int, b: int) -> int
        
        Dong's add function

FILE
    /workspace/dong/pybind_tt/example.cpython-37m-x86_64-linux-gnu.so

3.2 类的绑定

3.2.1 example.cpp

#include <pybind11/pybind11.h>

namespace py = pybind11;

struct Pet {
    Pet(const std::string &name) : name(name) { }
    void setName(const std::string &name_) { name = name_; }
    const std::string &getName() const { return name; }

    std::string name;
};

PYBIND11_MODULE(example, m) {
    m.doc() = "pybind11 example by Dong"; // optional module docstring
    py::class_<Pet>(m, "Pet")
        .def(py::init<const std::string &>())
        .def("setName", &Pet::setName, "Set the name of my pet")
        .def("getName", &Pet::getName, "Get the name of my pet");
}

具体解释: 

  • py::class_<Pet>(m, "Pet"):定义了一个 Pybind11 类,它将 C++ 中的 Pet 类绑定到 Python 中名为 Pet 的一个新类。
  • py::init<const std::string &>() 为 Python 中的 Pet 类添加一个构造函数,接受 std::string 类型的参数。

编译方法和基础示例相同:

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

3.2.2 绑定 lambda 函数

Lambda 函数是一个小巧、专用的函数,直接在类绑定表达式中内联定义函数,简化了代码结构,使其更加清晰。

py::class_<Pet>(m, "Pet")
    .def(py::init<const std::string &>())
    .def("setName", &Pet::setName)
    .def("getName", &Pet::getName)
    .def("__repr__",
        [](const Pet &a) {
            return "<example.Pet named '" + a.name + "'>";
        }
    );

调用 print(p) 时,将调用 .def("__repr__", ...) 所定义的 lamda 函数。

lamda 函数语法:

1). 定义:[](const Pet &a) {...}

  • [] 是捕获列表,用于指定在 lambda 函数体中可以使用哪些外部变量。在这个例子中,捕获列表为空,因为不需要捕获任何外部变量。
  • const Pet &a 是函数的参数列表,表示这个函数接受一个对Pet类型的常量引用作为参数。

2). 函数体: { return "<example.Pet named '" + a.name + "'>"; }

  • 此函数体只包含一个返回语句,它构造并返回一个字符串。
  • 包含固定的文本<example.Pet named >。
  • name 是 Pet 对象的属性。

3.2.3 帮助信息

Help on module example:

NAME
    example - pybind11 example by Dong

CLASSES
    pybind11_builtins.pybind11_object(builtins.object)
        Pet
    
    class Pet(pybind11_builtins.pybind11_object)
     |  Method resolution order:
     |      Pet
     |      pybind11_builtins.pybind11_object
     |      builtins.object
     |  
     |  Methods defined here:
     |  
     |  __init__(...)
     |      __init__(self: example.Pet, arg0: str) -> None
     |  
     |  getName(...)
     |      getName(self: example.Pet) -> str
     |      
     |      Get the name of my pet
     |  
     |  setName(...)
     |      setName(self: example.Pet, arg0: str) -> None
     |      
     |      Set the name of my pet
     |  
     |  ----------------------------------------------------------------------
     |  Static methods inherited from pybind11_builtins.pybind11_object:
     |  
     |  __new__(*args, **kwargs) from pybind11_builtins.pybind11_type
     |      Create and return a new object.  See help(type) for accurate signature.

FILE
    /workspace/dong/pybind_tt/example.cpython-37m-x86_64-linux-gnu.so

3.3 vart::Runner 类绑定

<Vitis-AI-2.5>/src/Vitis-AI-Runtime/VART/vart/runner/python/runner_py_module.cpp

PYBIND11_MODULE(MODULE_NAME, m) {
  m.doc() = "vart::Runner inferace";  // optional module docstring
  ...
  ...
  py::class_<vart::Runner>(m, "Runner")
  .def_static("create_runner",
              py::overload_cast<const xir::Subgraph*,
              const std::string&>(&vart::Runner::create_runner),
              py::arg("subgraph"),
              py::arg("mode") = "")
  .def("get_input_tensors",  &vart::Runner::get_input_tensors,  py::return_value_policy::reference)
  .def("get_output_tensors", &vart::Runner::get_output_tensors, py::return_value_policy::reference)
  .def("execute_async", []( vart::Runner* self,
                            std::vector<py::buffer> inputs,
                            std::vector<py::buffer> outputs,
                            bool enable_dynamic_array ) {
        // NOTE: it is important to initialize cpu_inputs and
        // cpu_outputs with GIL protection. the_map is the global
        // variable alike.
        auto cpu_inputs  = array_to_tensor_buffer(inputs , self->get_input_tensors() , enable_dynamic_array);
        auto cpu_outputs = array_to_tensor_buffer(outputs, self->get_output_tensors(), enable_dynamic_array);
        auto ret = make_pair(uint32_t(0), int32_t(0));
        if (1) {
          py::gil_scoped_release release;
          ret = self->execute_async(cpu_inputs, cpu_outputs);
        }
        // obtain the GIL again.
        if (ret.first >= 0) {
          for (auto t : cpu_inputs) {
            static_cast<CpuFlatTensorBuffer*>(t)->save_to_map(self, ret.first);
          }
          for (auto t : cpu_outputs) {
            static_cast<CpuFlatTensorBuffer*>(t)->save_to_map(self, ret.first);
          }
        } else {
          destroy(cpu_inputs);
          destroy(cpu_outputs);
        }
        return ret;
      },
      py::arg("inputs"),
      py::arg("outputs"),
      py::arg("enable_dynamic_array") = false)

  .def("wait", [](  vart::Runner* self,
                    std::pair<uint32_t, int> job_id) {
         auto ret = self->wait(job_id.first, -1);
         auto the_map = get_store();
         // copy instead of reference, it is important, do not use
         // reference here, the decontructor will clean up the mess.
         auto v = (*the_map)[self][(int)job_id.first];
         for (auto t : v) {
           delete t;
         }
         return ret;
       })
  .def("__repr__", [](const vart::Runner* self) {
    std::ostringstream str;
    str << "vart::Runner@" << (void*)self;
    
    return str.str();
  });

详细解释:

  • py::class_<vart::Runner>(m, "Runner"),创建了一个名为 Runner 的类,该类映射到 C++ 的vart::Runner 类。
  • .def_static("create_runner",...),绑定了 create_runner 静态方法,该方法可以使用 xir::Subgraph 和一个可选的字符串参数 mode 来创建一个 Runner 实例。
  • 获取输入和输出张量的方法:get_input_tensors、get_output_tensors。
  • .def("execute_async"...),异步执行方法,绑定了 execute_async 方法,该方法接受输入和输出缓冲区,并异步执行计算。使用了 py::gil_scoped_release 来释放 GIL(全局解释器锁),以便在执行异步操作时不阻塞 Python 解释器。
  • .def("wait",...),绑定了 wait 方法,该方法等待指定的作业完成。
  • .def("__repr__",...),绑定了 __repr__ 方法,用于返回对象的字符串表示。

4. 总结

pybind11 是一个轻量级、非侵入性的库,可以让 C++ 和 Python 之间的交互变得简单而高效。它不仅支持自动类型转换和高级 C++ 特性,还具有极低的运行时开销,使得开发者能够在保持 Python 易用性的同时,充分利用 C++ 的性能优势。

本篇文章将深入探讨 pybind11 的核心功能和用法,包括如何通过魔法命令在 Jupyter Notebook 中快速编译和加载 C++ 代码,如何使用 pybind11 进行模块和类的绑定,以及在实际应用中如何利用这些功能实现高效的数据处理和硬件控制。通过这些内容,我们希望能够帮助读者更好地理解和应用 pybind11,从而在其项目中实现更高的性能和灵活性。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/872341.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

java 设计模式-代理模式

目录 概述 一. 什么是代理模式 1. 举例说明 二. 代理模式作用 1. 保护代理 2. 增强功能 3. 代理交互 4. 远程代理&#xff1a; 三. 代理模式3个角色 四. 静态代理 1. 代码示例&#xff1a; 五. JDK动态代理 1. 代码示例&#xff1a; 六. CGLIB 动态代理 1.代码示…

P1.8COB小间距成本下降,增大COB超微小间距LED显示屏市场份额

随着P1.8 COB&#xff08;Chip On Board&#xff09;小间距技术的不断成熟与成本的有效控制&#xff0c;其在LED显示屏市场中的竞争力日益凸显。这一趋势不仅激发了行业内对超微小间距LED显示屏的浓厚兴趣&#xff0c;更推动了市场的快速扩张。 随着成本的进一步下降&#xff…

支持图片和视频分割,SAM2最新分割一切大模型分享

Segment Anything Model 2&#xff08;简称SAM 2&#xff09;是由Meta&#xff08;Facebook AI&#xff09;开发的最新一代图像和视频分割模型。 SAM2能够实现对静态图像和动态视频中的对象进行实时、可提示的分割&#xff0c;将图像与视频分割功能整合到了同一个系统中。 SA…

在Centos中的mysql的备份与恢复

1.物理备份 冷备份&#xff1a;关闭数据库时进行热备份&#xff1a;数据库运行时进行&#xff0c;依赖于数据库日志文件温备份&#xff1a;数据库不可写入但可读的状态下进行 2.逻辑备份 对数据库的表或者对象进行备份 3.备份策略 完全备份&#xff1a;每次都备份完整的数…

【运维监控】influxdb 2.0+telegraf 监控tomcat 8.5运行情况(1)

关于java应用的监控本系列有文章如下&#xff1a; 【运维监控】influxdb 2.0telegraf 监控tomcat 8.5运行情况 【运维监控】influxdb 2.0grafana 监控java 虚拟机以及方法耗时情况 【运维监控】Prometheusgrafana监控tomcat运行情况 【运维监控】Prometheusgrafana监控spring b…

数学建模强化宝典(13)M-K检验法

前言 M-K检验法&#xff0c;全称为Mann-Kendall检验法&#xff0c;是一种非参数的假设检验方法&#xff0c;广泛应用于时间序列数据的趋势性变化检验&#xff0c;特别是气候序列中的趋势分析和突变点检测。以下是对M-K检验法的详细介绍&#xff1a; 一、定义与背景 M-K检验法由…

Leetcode面试经典150题-83.删除链表中的重复元素

解法都在代码里&#xff0c;不懂就留言或者私信 /*** Definition for singly-linked list.* public class ListNode {* int val;* ListNode next;* ListNode() {}* ListNode(int val) { this.val val; }* ListNode(int val, ListNode next) { this.val …

服务器监控工具都是监控服务器的哪些性能和指标

服务器监控工具通常用于确保服务器及其相关服务的正常运行。这些工具可以帮助管理员快速识别并解决问题&#xff0c;从而减少停机时间和性能下降的风险。以下是服务器监控工具通常会监控的一些主要内容&#xff1a; 系统健康状态&#xff1a; CPU使用率 内存&#xff08;RAM&…

油猴插件录制请求,封装接口自动化参数

参考&#xff1a;如何使用油猴插件提高测试工作效率 一、背景 在酷家乐设计工具测试中&#xff0c;总会有许多高频且较繁琐的工作&#xff0c;比如&#xff1a; 查询插件版本&#xff1a;需要打开Chrome控制台&#xff0c;输入好几个命令然后过滤出版本信息。 查询模型商品&…

JAVA—单元测试

单元测试&#xff1a;就是针对最小的功能单元&#xff08;方法&#xff09;&#xff0c;编写测试代码对其进行正确性测试 之前是使用main函数调用来进行检测&#xff0c;无法实现自动化测试 也会影响其他方法的测试 目录 1.junit框架概述 2.junit框架的常见注解 1.junit框架…

基于C++实现(MFC界面)家谱管理系统

一、题目&#xff1a;家谱管理系统 二、内容&#xff1a; 2.1 概述 2.1.1 选题原因 做此题的原因是因为可以比较方便的记录家族历代成员的情况与关系&#xff0c;能很好的保存家族每一代的信息&#xff0c;而不用人工纸质的方式来存取家谱&#xff0c;更便于人们保存和使用…

基于STELLA系统动态模拟技术及在农业、生态环境等科学领域中的实践应用

STELLA是一种用户友好的计算机软件。通过绘画出一个系统的形象图形&#xff0c;并给这个系统提供数学公式和输入数据&#xff0c;从而建立模型。依据专业兴趣&#xff0c;STELLA可以用来建立各种各样的农业、生态、环境等方面的系统动态模型&#xff0c;为科研、教学、管理服务…

Day16_0.1基础学习MATLAB学习小技巧总结(16)——元胞数组

利用空闲时间把碎片化的MATLAB知识重新系统的学习一遍&#xff0c;为了在这个过程中加深印象&#xff0c;也为了能够有所足迹&#xff0c;我会把自己的学习总结发在专栏中&#xff0c;以便学习交流。 素材来源“数学建模清风” 特此说明&#xff1a;本博客的内容只在于总结在…

linux top命令介绍以及使用

文章目录 介绍 top 命令1. top 的基本功能2. 如何启动 top3. top 的输出解释系统概况任务和 CPU 使用情况内存和交换空间进程信息 4. 常用操作 总结查看逻辑CPU的个数查看系统运行时间 介绍 top 命令 top 是一个在类 Unix 系统中广泛使用的命令行工具&#xff0c;用于实时显示…

pikachu文件包含漏洞靶场攻略

1.File inclusion(local)&#xff08;本地文件包含&#xff09; 提交一个球员信息 filename后面输入../../../../../1.php访问php文件 2.File Inclusion(remote)&#xff08;远程文件包含&#xff09; 修改配置 远程包含漏洞的前提&#xff1a;需要php.ini配置如下&#…

深入理解 Babel - 微内核架构与 ECMAScript 标准化|得物技术

随着浏览器版本的持续更新&#xff0c;浏览器对JavaScript的支持越来越强大&#xff0c;Babel的重要性显得较低了。但Babel的设计思路、背后依赖的ECMAScript标准化思想仍然值得借鉴。 本文涉及的Babel版本主要是V7.16及以下&#xff0c;截至发文时&#xff0c;Babel最新发布的…

利用SSH加密实现的HTTP隧道分析与检测

1.隧道介绍 Chisel是一个快速稳定的TCP/UDP隧道工具&#xff0c;该工具基于HTTP实现&#xff0c;并通过SSH加密保证通信安全。Chisel可以进行端口转发、反向端口转发以及SOCKS流量代理&#xff0c;使用GO语言编写&#xff0c;具备较好的跨平台特性。该工具的主要用于绕过防火墙…

Hive数据库与表操作全指南

目录 Hive数据库操作详解 创建数据库 1&#xff09;语法 2&#xff09;案例 查询数据库 1&#xff09;展示所有数据库 &#xff08;1&#xff09;语法 &#xff08;2&#xff09;案例 2&#xff09;查看数据库信息 &#xff08;1&#xff09;语法 &#xff08;2&#…

Spring之整合Mybatis底层源码解析

整合核心思路 由很多框架都需要和Spring进行整合&#xff0c;而整合的核心思想就是把其他框架所产生的对象放到Spring容器中&#xff0c;让其成为Bean。 ​ 比如Mybatis&#xff0c;Mybatis框架可以单独使用&#xff0c;而单独使用Mybatis框架就需要用到Mybatis所提供的一些类…

学习笔记八:基于Jenkins+k8s+Git+DockerHub等技术链构建企业级DevOps容器云平台

基于Jenkinsk8sGitDockerHub等技术链构建企业级DevOps容器云平台 测试jenkins的CI/CD在Jenkins中安装kubernetes插件安装blueocean插件配置jenkins连接到我们存在的k8s集群配置pod-template添加自己的dockerhub凭据测试通过Jenkins部署应用发布到k8s开发环境、测试环境、生产环…