为什么大家都在用Rust提升Python性能?真相让人意想不到!

400 阅读5分钟

图片

创作不易,方便的话点点关注,谢谢

本文是经过严格查阅相关权威文献和资料,形成的专业的可靠的内容。全文数据都有据可依,可回溯。特别申明:数据和资料已获得授权。本文内容,不涉及任何偏颇观点,用中立态度客观事实描述事情本身。

文章结尾有最新热度的文章,感兴趣的可以去看看。

文章有点长(3337字阅读时长:10分),期望您能坚持看完,并有所收获


图片

(本图片来源真实素材)

大家好,今天我继续给大家分享干货。熟悉我的人,都知道我真正的干货一般在中间和末尾部分。请耐心看完!谢谢。

本文将介绍如何搭建一个结合Python和Rust代码的混合项目。为何要结合?Python的解释型特性和动态类型能加快我们的开发流程。但面对更复杂的任务时,我们常常会遇到性能问题。

Rust是提升Python项目原生性能的理想选择。原生优化利用低级语言和编译器绕过Python解释器。通过结合Rust和Python,你能够兼取二者之长:既能利用Python获得快速且交互式的开发环境,又能借助Rust实现原生性能。

使用PyO3 + Maturin

图片

将Rust与Python集成的主要方式是借助PyO3框架。利用PyO3,我们可以用Rust编写原生Python模块。当然,该框架也支持从Rust调用Python,但在这里我将只专注于用原生Rust模块扩展Python。

PyO3会将你的Rust代码封装成一个原生Python模块。这样一来,绑定生成既简便又完全透明。

麻烦的部分在于构建Python与原生代码(Python + [Rust、C或C++])混合项目时的代码打包问题。那么,如何制作整合Python与原生代码的wheel包呢?

Python代码无需编译即可分发,并且与平台无关;安装wheel包时会即时生成.pyc文件(Python字节码)。但我们的Rust代码需要编译并作为共享库(二进制代码)进行分发。

打包是这个项目的难点:创建一种通用的、跨平台的方式来生成Python-Rust混合包。

能帮你实现这一目标的工具是Maturin。Maturin负责管理创建、构建、打包以及分发Python/Rust混合项目。

使用Maturin初始化混合项目

图片

首先,使用pip安装Maturin Python包。

pip install maturin
它包含了maturin二进制文件,这是一个命令行界面。

$ maturin --help

maturin 0.12.9
Build and publish crates with pyo3, rust-cpython and cffi bindings as well as rust binaries as
python packages

USAGE:
    maturin <SUBCOMMAND>

OPTIONS:
-h,--help       Print help information
-V,--version    Print version information

SUBCOMMANDS:
    build          Build the crate into python packages
    develop        Installs the crateas module in the current virtualenv
    help           Print this message or the help of the given subcommand(s)
    init           Create a new cargo project in an existing directory
    list-python    Searches and lists the available python installations
    new            Create a new cargo project
    publish        Build and publish the crateas python packages to pypi
    sdist          Build only a source distribution(sdist) without compiling
    upload         Uploads python packages to pypi

maturin可执行文件提供了几个选项。让我们聚焦于build和develop这两个选项。

  1. build:编译Rust代码并将其与Python包集成。它会生成一个wheel包,将Rust生成的二进制文件与最终的Python代码混合在一起。

  2. develop:在开发和调试项目时非常有用。此命令构建新创建的共享库并直接将其安装到Python模块中。

为了测试Rust-Python混合项目,现在我们可以使用maturin new命令初始化我们的库。

$ maturin new --help

maturin-new
Create a new cargo project

USAGE:
    maturin new [OPTIONS]<PATH>

ARGS:
<PATH>Project path

OPTIONS:
-b,--bindings <BINDINGS>Which kind of bindings to use[possible values: pyo3, rust-cpython,
                                 cffi, bin]
-h,--help                   Print help information
--mixed                  Use mixed Rust/Python project layout
--name <NAME>Set the resulting package name, defaults to the directory name

对于我们的项目,通过使用选项 --bindings pyo3和 --mixed my_project来初始化一个带有PyO3绑定的Python/Rust混合项目。这里的my_project参数与本示例的目标项目目录相对应。

maturin new --bindings pyo3 --mixed my_project
生成的项目在my_project目录中集成了一个Python包、一个Rust项目定义文件Cargo.toml以及一个基于Rust的src目录。

$ tree my_project
  
my_project
├──Cargo.toml
├── my_project
│└── __init__.py
├── pyproject.toml
├── src
│└── lib.rs
└── test
└── test.py

3 directories,5 files

好了,我们已经有了基本的项目框架。现在可以添加一个简单的Rust函数来对外暴露了。

使用PyO3封装Python代码

我们需要在一个可被Python运行时调用的Python模块中声明并导出Rust函数。首先,使用#[pymodule] Rust宏创建一个模块。在我们的函数my_project中,将声明Rust与Python之间的绑定。

→ rust «my_project/src/lib.rs»=

use pyo3::prelude::*;(1)

(2)
<<functions>>

#[pymodule]
fnmy_project(_py:Python, m:&PyModule)->PyResult<()>{
(3)
<<function_declarations>>
Ok(())
}

(4)
<<tests>>

(1) 我们引入Py03的定义和宏。
(2) 在 <<函数>> 代码块中,声明我们的Rust函数。
(3) 在 <<函数声明>> 代码块中,在最终的Python模块中暴露我们的Rust函数。
(4) 在 <<测试>> 代码块中,添加Rust单元测试函数。

一个简单的函数

让我们从为 <<函数>> 代码块编写一个简单函数开始。我们要导出的函数is_prime通过用前面的数去除输入值来检查其是否为质数。对于一个数num,我们检查2到√num之间的数相除的余数。

→ rust «functions»=

#[pyfunction](1)
fnis_prime(num:u32)->bool{
match num {
0|1=>false,
        _ =>{
letlimit=(num asf32).sqrt()asu32;(2)

(2..=limit).any(|i| num % i ==0)==false(3)
}
}
}

(1) Rust宏#[pyfunction]会生成用于Python绑定的代码。
(2) 计算我们试除序列的上限。
(3) 进行试除并测试余数。2..=limit生成一个从2到limit(包含)的范围。any函数检查生成的元素中是否有一个满足条件。
在 <函数声明> 代码块中,将我们的函数添加到要导出的模块中。

→ rust «function_declarations» =
  
m.add_function(wrap_pyfunction!(is_prime, m)?)?;

在 <<测试>> 代码块中,添加一些简单的单元测试。

→ rust «tests»=

#[cfg(test)]
mod tests {
use super::*;

#[test]
fnsimple_test_false(){
assert_eq!(is_prime(0),false);
assert_eq!(is_prime(1),false);
assert_eq!(is_prime(12),false)
}

#[test]
fnsimple_test_true(){
assert_eq!(is_prime(2),true);
assert_eq!(is_prime(3),true);
assert_eq!(is_prime(41),true)
}
}

构建并运行你的Python模块

使用Maturin,构建原生Rust模块并在Python解释器中导出它只需一行命令。

$ cd my_project
$ maturin develop

这个命令构建原生Rust模块并将其部署到当前虚拟环境中。

import my_project
  
print(my_project.is_prime(12))
print(my_project.is_prime(11))
  
> FalseTrue

在幕后,maturin develop命令:

使用Cargo编译原生Rust模块:一个共享库被编译并复制到本地Python模块中。
安装Python模块:模块被安装到你的虚拟环境中。为了在每次Python代码更改时无需重新构建项目即可立即进行测试,你可以使用可编辑安装(即pip install -e),方法是在pyproject.yml(Maturin可编辑安装)中添加以下几行。

[build-system]
requires = ["maturin>=0.12"]
build-backend = "maturin"

测试你的模块

我们可以在Python中添加一个简单的基于属性的测试,以检查我们的is_prime Rust函数的行为。首先,为Hypothesis Python包添加一个依赖项。要向我们的项目添加依赖项,可以编辑pyproject.toml文件。

→ toml «my_project/pyproject.toml»=

[build-system]
requires =["maturin>=0.12"]
build-backend ="maturin"

[project.optional-dependencies]
test =[
"hypothesis",
"sympy"
]

[project]
name ="my_project"
requires-python =">=3.6"
classifiers =[
"Programming Language :: Rust",
"Programming Language :: Python :: Implementation :: CPython",
]

现在我们可以运行maturin develop命令。

$ cd my_project
$ maturin develop --extras test (1)

(1) 使用选项 --extras test,Maturin会安装Python测试依赖项。

我们添加一个简单的基于属性的测试。

→ Python«my_project/test/test.py»=

from hypothesis import settings,Verbosity, given
from hypothesis import strategies as st
from sympy.ntheory import isprime
import my_project

@given(s=st.integers(min_value=1, max_value=2**10))
@settings(verbosity=Verbosity.normal, max_examples=500)
def test_is_prime(s):
assertisprime(s)== my_project.is_prime(s)

if __name__ =="__main__":
test_is_prime()

最后,我们可以运行基于属性的Python测试。

$ cd my_project
$ python test/test.py

以及我们的Rust测试。

$ cd my_project
$ cargo test

总结

就是这样的!你的Python/Rust混合模块已经可以使用、部署和发布了。Maturin提供了几个命令来帮助编译和分发你的Rust/Python混合包。

有很多方法可以改进你的Python代码。可以直接在Python中使用Numba,或者在有类型的DSL中使用Cython。你可以使用绑定生成器暴露C/C++原生代码。但现在,借助PyO3,你可以在Python中受益于Rust语言的安全性以及原生性能。使用Maturin项目,创建Python + Rust混合项目变得很容易。最终项目被打包成一个包含Python代码和共享库的Python wheel包。

以上就是我的分享。这些分析皆源自我的个人经验,希望上面分享的这些东西对大家有帮助,感谢大家!

图片

点个“在看”不失联

最新热门文章推荐:

深度解析:Rust智能指针在多场景应用的,轻松应对复杂编程

必看!Discord从Go切换到Rust语言背后带来提升性能并降低延迟

深度解析C语言指针,三大实用场景助你编程升级!

用C语言重现Linux中mkdir命令,你绝对想不到如此简单!

C语言模拟多态性:结构体与函数指针的巧妙运用

从零开始:用C语言打造简化版GCC,探索编译器工作原理!

写给 C++ 程序员:一文读懂悬空指针,如何巧妙避开

惊!卷积神经网络挑战低光照图像增强难题

必看!C语言面试高频编程题及详细解析

使用Rust实现神经网络:不依赖PyTorch,170行代码训练一个二分类器准确率高达99%,你敢信?

打破语言壁垒:C与Go融合开发效率大幅提升

Rust生产实践中的那些意想不到的优势,你绝对想不到!

告别C/C++,用Rust打造更安全高效的嵌入式系统!

2024年12月TIOBE指数揭示:PHP等语言前景堪忧

2025开发者必看:Rust与Go对决,谁更胜一筹?

用YOLOv8和OpenCV打造车道与车辆检测系统,实时掌握道路情况!

OpenCV全景图拼接:全景图制作竟如此简单

谁才是Rust视频处理的最佳选择?ffmpeg-next vs opencv vs video-rs

参考文献:《Py03》《Maturin》

本文使用 文章同步助手 同步