Rust 开发系列PyO3:Rust与Python的联动编程(上)

Posted 虾神说D

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Rust 开发系列PyO3:Rust与Python的联动编程(上)相关的知识,希望对你有一定的参考价值。

前言

Rust语言经常被人误认为是R语言,或者Ruby语言……但是做为近十年来tiobe最出人意料的编程语言,从冷门逐渐变成了明星,不过这次我们不讲Rust入门的内容,我们先来看看它一个很实用的功能——与Python的联动编程

在正式开始之前,可以先看以下几个问题:

  • 为什么要用Rust?
  • 答:Rust有很多优点,也有很多缺点,但是下面这个优点可以覆盖所有的缺点:即它能够编写出保证永远不出现内存错误的程序!!

 编程领域最头痛的问题,就是内存错误,这种错误无法排查,无法重现,随机出现,只能靠着程序员的编码能力,在编写代码的时候小心翼翼的检查以排除,所以C/C++虽然号称也可以写出内存安全的程序,但是实际上在大型程序中,几乎是一种可望不可及的理想状态。 而Rust以其特有的所有权设计思想,保证了在任何情况下的内存安全,只要编译通过,就永远不会出现内存错误。

  • 注意:不出现内存错误,不代表永远不会出现错误,而是出现任何错误,Rust都会告诉为什么会错,不会仅仅弹出一个不知所云的内存问题,让你无从下手,无从排查,也无法解决。

正文

全文一共4节,大约分为三章讲完,内容如下

  • 第一节:PyO3的简介,聊一下Python的联合开发方式,以及什么是PyO3
  • 第二节:从0开始编写一个PyO3程序,并且在Python中进行调用
  • 第三节:介绍PyO3提供的官方的脚手架,让我们如何快速的创建和编译Python扩展项目
  • 第四节:编写一系列计算密集型的插件,并且与Python原生代码和Python的JIT优化模式进行效率对比

第一节:Python扩展开发与PyO3简介

Python的优点,数不胜数,但是如果要说Python的缺点,那么下面这个应该是所有Python程序员最大的痛点:

Python有多慢呢? 下面这个是葡萄牙米尼奥大学和葡萄牙科英布拉大学联合的HAS Lab/INESC TEC(高性能计算实验室)在2020年11月发表的一篇论文,论文中对流行的编程语言进行了基准性能测试:

可以看见,无论是性能、时间、还是内存消耗,Python都几乎是倒数,特别是在计算性能和消耗时间上,几乎只有C、Rust、C++这三者的七十分之一!

当然,这里说个题外话,高性能有若干前提要求,不是说Python本身慢,写出来的东西就一定很慢,Python有大量优化方式和手段,例如静态编译(JIT),或者使用numpy这种高性能包,都能够大大提升Python的性能。

另外,程序运行的模式,也会对代码编写和架构有很大的影响,一般来说,在IO(数据)密集型运算上,管你用什么语言,性能都只能被数据吞吐这块短板所拖累……今天不谈这些内容,今天我们主要就针对计算(CPU)密集型运算这种与编程语言和编译器有密切关系的方式。

所以,抛开一切其他条件不谈,我们的诉求,就是一句话:

速度!
速度!!
还TM是速度!!

如果说Python原生的速度,就像体重和身高一样的正方体肥仔虾一样,从小学开始到大学毕业,体育考试中的长跑就从来没有及格过,基本上等于放弃治疗的话,如何最简单的提速呢?

请圣言

人和动物的区别在于,人能够创造工具,动物可以使用工具,但不能创造工具。

——卡尔 马克思

回过来,肥宅Py如何利用工具,变成狂飙Py呢?这就要从Python的扩展开发模式来看了:

首先,我们经常说的,Python,实际上指的是CPython,即C语言实现的Python,广义上,Python还有Jython和IronPython这两个版本,分别是Java的Python实现和.Net的Python实现,不过后面这两个东西,已经很久没有更新了,属于死了但是没有完全死的状态,基本上我们可以忽略……

所以,现在我们说的Python,事实上指的就是CPython,所以可以认为Python的底层就是C语言,那么官方提供的Python的扩展方式,就是用C语言来编写扩展模块(Python的标准库,绝大部分都是C语言开发的,C++属于蹭C语言的框架)。

其他语言如果扩展Python,只能通过如RPC(远程过程调用)这种方式,或者语言扩展桥接绑定的方式来实现,这两种模式就不能叫做扩展开发了。

那么Rust对于Python的扩展开发,又是哪一种方式呢? 答案是基于C语言的桥接模式。Rust可以直接继承C/C++的所有工具和库,也能够支持把自己编译成与C一样的动态链接库,所以我们可以把PyO3看成是Rust绑定C语言的一种扩展方式,也就是说,Rust PyO3写出来的扩展,与原生C写出来的扩展,对于Python是完全一样的。

PyO3开发出来的扩展模块,有如下特点:

  1. 开发出来的扩展模块,运行于Python同进程中,所以可以直接在Python语言环境中导入、调用、监控和管理。
  2. 可以在Rust中编写的、编译好的可执行程序中,调用和动态执行Python代码的能力,并且提供两种语言之间的控制与数据交互能力。

第二节:Python扩展开发与PyO3简介

首先还是用Rust的cargo包管理器去创建一个lib工程,这一步没啥说的。

如果你是一位从来没有接触过Rust的同学,那么可以先不必深究这些细节,建议直接看后面的结果。 然后就采用量子学习法即可:

  • 点赞就是看过了

  • 收藏就是学会了

  • 转发就是融会贯通了

然后在crates.io网站上,找到PyO3包,并且把它添加到配置文件里面去。当然,如果你很熟练了,也不用去网站上找,直接添加也行(建议去网站上,查找最新的版本号。)

crates.io是Rust的一个包管理仓库网站,类似于Python的pypi。

添加配置项,注意,需要有两个必选的配置项:

crate-type = ["cdylib"]

这个表示编译时候使用的c标准的动态库 Python的底层就是用c语言写的,必须是c标准库,Python才能导入

pyo3 = version = "0.18.1", features = ["extension-module"]

给工程添加最新的pyo3版本,并且设置特性为扩展模块,这个版本号一般我都选择最新的,所以在添加前都回去crates.io网站上查阅一下,不过你也可以通过

cargo add pyo3

这个命令来添加

之后,就可以编写Rust的功能实现代码了,主要需要编写两个部分: 

  1. 编写逻辑业务实现部分,我们这里实现一个say hello的功能,也就是用户输入一个用户名,这里输出一个问候,并且告诉他,这个问候来自Rust编写的后台扩展。

如果需要直接封装成Python可以调用的方法,需要在前面加#[pyfunction]这个属性,表示这个方法将对外暴露,并且被直接封装成Python可以调用的方法。

  1. 可以写一个测试方法,因为Rust是一种编译执行的语言,所以要执行一个方法必须要有main函数,如果不想添加main.rs和main函数的话,可以写一个测试方法,在cargo里面测试用。
  2. 把写好的Python扩展方法,封装到Python模块声明里面去,写一个Python模块声明方法。

#[pymodule]属性表示这个模块里面,要封装暴露哪些方法出来,而且还声明了是否有输入和输出参数。

全部写完之后,可以通过cargo test测试一下,方法是否能够正常执行,之后就可以打包封装了。

直接输入命令:

cargo build --release

对工程进行编译,在这个编译的过程中,cargo编译器会从网络上拉下一些依赖,然后在本地直接编译好,但是这个过程比较慢……特别是大型工程,一次编译可能需要几十分钟之久。

不过好在我们这个工程中没有什么需要依赖的大型库,所以编译还算快,在我本机上,11秒就编译好了。

之后,在工程的/target/release目录下面,会生成一个叫做pyo3demo.dll的动态链接库文件,这个就是windows平台下面的Python扩展模块,如果在Linux下面,会编程成so文件。

因为我们这里是一个裸奔的版本,所以后面还需要手动改一下名字,在Python中,扩展库的的后缀名都是pyd,所以需要我们手动把pyo3demo.dll改名为pyo3demo.pyd。

之后把这个pyd文件,拷贝到你的Python包索引环境的目录下面去,就可以了,最简单的就是直接拷贝到你py文件同目录下。

然后按照Python开发的一般模式,import这个包,然后直接使用即可:

如果输出的内容是我们测试中预想一样的,那就表示成功了。

最后,给出这个say hello应用中可能出现的一些场景问题:

打完收工。

最后,本工程的全部源码,在我gitee仓库如下位置:

博客: 博客文章,方便发文 - Gitee.com

注意: 我的代码里面还有后续的测试的内容,特别包含了polars包,所以编译起来比较慢,也特别大……

以上是关于Rust 开发系列PyO3:Rust与Python的联动编程(上)的主要内容,如果未能解决你的问题,请参考以下文章

Rust 开发系列PyO3:Rust与Python的联动编程(下)

rust拓展包是什么内容

[from js to rust 系列][宏-01][官网文档 19.5]高级特性:宏[译文]

C++&Rust对比C++学习和运用Rust语言系列文章

C++&Rust对比C++学习和运用Rust语言系列文章

C++&Rust对比C++学习和运用Rust语言系列文章