跳转至

屠龙之技! 使用Rust加速你的Python

前言

默认读者已经习得 rust基础相关, 且掌握python基础语法

资料不多, 我也是小试牛刀

测试结果有一点疑问, 在长度越长的时候,使用指针操作数组,速度反而下降

1. 配置编译环境

1.1 创建rust模块

cargo new handle --lib

1.2 添加相应包

// Cargo.toml 添加

[lib]
name = "handle"  // python中导入的名字,这里注意要和我们打包后的文件名一致
crate-type = ["cdylib"]

[dependencies.cpython]  // rust打包python支持的so主要库
version = "0.5.*"
features = ["extension-module"]

2. rust代码实现

// lib.rs


use cpython::{PyResult, Python, py_module_initializer, py_fn, PyList, PyObject, PyDict};

                // 注意此处名称和文件名统一
py_module_initializer!(handle, |py, m| {
    m.add(py, "__doc__", "This module is implemented in Rust.")?;
    m.add(py, "sum_as_string", py_fn!(py, sum_as_string_py(a: i64, b:i64)))?;  // 注册
    m.add(py, "bubble1", py_fn!(py, bubble1(li:Vec<i32>)))?;  // 注册
    m.add(py, "bubble2", py_fn!(py, bubble2(li:Vec<i32>)))?;  // 注册
    Ok(())
});

fn sum_as_string(a:i64, b:i64) -> String {  // 
    format!("{}", a + b).to_string()
}
fn sum_as_string_py(_: Python, a:i64, b:i64) -> PyResult<String> {
    let out = sum_as_string(a, b);
    Ok(out)
}

// 做成泛型, 当在动态运行语言中运行的时候,性能较i32下降 3%(应该是打包so时编译器无法优化), 这里使用了 i32 
fn bubble1(py: Python, mut li:Vec<i32>) -> PyResult<Vec<i32>>
{
    let n = li.len();
    for i in 0..n{
        for j in 0..(n-i-1) {
            if li[j] > li[j+1]{
                let temp = li[j + 1];
                li[j + 1] = li[j];
                li[j] = temp;
            }
        }
    }
    Ok(li)
}

fn bubble2(py: Python, mut li:Vec<i32>) -> PyResult<Vec<i32>>
{
    let li = &mut li;
    let n = li.len();
    for i in 0..n {
        for j in 0..(n - i - 1) {
            if li[j] > li[j + 1] {
                let temp = li[j + 1];
                li[j + 1] = li[j];
                li[j] = temp;
            }
        }
    };
    Ok(li.to_vec())
}

2.1 build

cd handle
cargo build --release
cd target/release
mv ./libhandle.dylib ./handle.so  // 注意编译之后需要手动重命名(!mac后缀为dylib需要改为so, win上为dll需要改为pyd)

python相关代码

条件: 长度1w 其内元素类型为i32类型的数组, 冒泡排序10次

import random
import time

# 此处默认py文件和编译且改名后的handle文件在同一目录
from handle import bubble2,bubble1


def get_li():
    li = []
    for i in range(10000):
        li.append(random.randint(1, 100))
    return li


def bubbleSort(arr):
    n = len(arr)
    for i in range(n):
        for j in range(0, n - i - 1):
            if arr[j] > arr[j + 1]:
                arr[j], arr[j + 1] = arr[j + 1], arr[j]
li = get_li()
st = time.perf_counter()
for i in range(10):
    bubbleSort(li)
print(time.perf_counter() - st)

li = get_li()
st = time.perf_counter()
for i in range(10):
    bubble1(li)
print(time.perf_counter() - st)

li = get_li()
st = time.perf_counter()
for i in range(10):
    bubble2(li)
print(time.perf_counter() - st)
>>> python: 46.073884827
>>> rust bubble1: 0.5787298269999965
>>> rust bubble2: 0.9453265779999995