rust加速python

6 篇文章 0 订阅

背景

  1. 之前都是在看C/Cpp,也在努力的学这两个语言。但是感觉太难了。语法太多了(主要还是人笨,学不过来)。
  2. 一直也在看rust,感觉rust写起来和python差不多优雅,而且写法和c++又是有点类似。
  3. 然后最近空闲时间就把《Rust程序设计语言》和《通过例子学Rust》两本书看完了。学到了不少新内容。
  4. 最近也发现一些新的包,比如一个叫polar包的,提供类pandas的功能,但是比pandas更快,这个包的底层是使用rust写的,并且python也能使用。那我就在想:“我能不能学习一下,看看怎么让python用rust编译的东西,或者说,如果使用rust加速python。” 这篇文章主要就是介绍一个简单的rust加速python的案例。(主要是翻译,原文已放在文末的参考链接中)。

步骤

创建一个rust包

cargo new pyext-myrustlib

然后使用vscode进入这个文件夹下,打开src文件夹,创建一个新文件:lib.rs

编辑Cargo.toml

  1. 依赖的是rust-cpython。当前版本是0.7(2022年4月)。
  2. 输出的是一个dylib。这个可以让python直接import。
[package]
name = "pyext-myrustlib"
version = "0.1.0"
edition = "2021"


[lib]
name="myrustlib"
crate-type=["dylib"]

[dependencies.cpython]
version="0.7"
features=["extension-module"]

编写src/lib.rs

  1. cpython导入宏。
  2. 调用Python,PyResult
  3. 构建一个count_doubles函数。
  • 3.1 这个函数第一个参数是Python,是对python解释器的引用,可以让rust使用python的GIL。
  • 3.2 函数的第二个参数是val,是一个字符串的引用。
  • 3.3 返回的对象是PyResult。即使到时候有异常,也可以让这个函数报错。
  • 3.4 更多的细节,其实可以看看rust官网上两本书。
  1. 使用宏py_module_initializer!给这个lib注册一个新的属性。比如这里就是给count_doubles函数添加了一个函数文档。
#[macro_use]
extern crate cpython;

use cpython::{Python, PyResult};

fn count_doubles(_py:Python, val:&str) -> PyResult<u64>{
    let mut total = 0u64;

    // there is an imporved version later on this post
    for (c1, c2) in val.chars().zip(val.chars().skip(1)) {
        if c1 == c2 {
            total += 1;
        }
    }
    Ok(total)
}


py_module_initializer!(libmyrustlib, initlibmyrustlib, PyInit_myrustlib, |py, m | {
    m.add(py, "__doc__", "This module is implemented in Rust")?;
    m.add(py, "count_doubles", py_fn!(py, count_doubles(val: &str)))?;
    Ok(())
});

build

  1. build一下
cargo build --release
  1. 看输出的结果

build后,会产生一个文件:

ls -la target/release/libmyrustlib*

看到上面的结果,就知道,这个时候已经生成了sod格式的文件。

接下来,把这个so结尾的文件复制到一个新的文件夹中。

我就是把这个so结尾的文件放在了mypythoncode文件夹中。

运行python

我在mypythoncode文件夹,还创建了doubles.py文件,用来做评测。这个文件的代码如下:

import re
import string
import random
import libmyrustlib   #  <-- 这里就是我们要import的rust的打包后的文件 (libmyrustlib.so)


def count_doubles(val):
    """Count repeated pair of chars ins a string"""
    total = 0
    for c1, c2 in zip(val, val[1:]):
        if c1 == c2:
            total += 1
    return total


double_re = re.compile(r'(?=(.)\1)')


def count_doubles_regex(val):
    return len(double_re.findall(val))


val = ''.join(random.choice(string.ascii_letters) for i in range(1000000))


def test_pure_python(benchmark):
    benchmark(count_doubles, val)


def test_regex(benchmark):
    benchmark(count_doubles_regex, val)


def test_rust(benchmark):   #  <-- 测试rust打包的函数的速度
    benchmark(libmyrustlib.count_doubles, val)

接下来我们比较各个函数的运行效率。
如果没有安装pytest。需要先安装一下,不然python程序跑不起来:pip install pytest-benchmark.

benchmark

mypythoncode文件夹下,运行:

pytest doubles.py

在mean这一列,会发现rust写的函数运行时间python写的函数运行时间的1/25倍左右。差距非常大。

注意事项:⚠️

  1. 本文不是一个比较性能的文章。因此上面的运行效率参考可能没什么价值,实际上,如果使用numpy计算的话,时间应该是rust版本的2倍。而不是25倍。
  2. 大家最好看一下原作者的内容【在参考链接[1] 中】. 原作者中一下代码和依赖的版本和现在不一样了,如果要说看代码部分,看我这个部分就行了。

参考链接

  1. https://developers.redhat.com/blog/2017/11/16/speed-python-using-rust#edit_cargo_toml
  2. https://crates.io/crates/cpython
  3. https://pypi.org/project/pytest-benchmark/

感悟

  1. 目前还在学习rust中,只是把《Rust程序设计语言》和《通过例子学Rust》两本书看完了,代码都敲了一遍。说实话,还是非常喜欢这个语言的。感觉大部分代码写法和python差不多。后面应该还是会继续深入学习。
  2. 未来会继续把时间投资在pythonrust上。学习算法、学习科学计算。
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

yuanzhoulvpi

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值