用 Rust 重写 Python 脚本:性能提升 100 倍的真实案例

10次阅读
没有评论

起因:一个跑了 20 分钟的脚本

上个月,我遇到了一个性能瓶颈问题。我有一个 Python 脚本,负责处理一批 CSV 文件——读取、解析、做数据清洗、计算统计指标、然后输出结果。数据量大约是 500 万行,分布在 200 多个 CSV 文件中。这个脚本每次运行需要大约 20 分钟。

20 分钟听起来不算特别长,但问题是这个脚本需要每天运行一次,而且是数据处理流水线中的一个关键环节。下游的分析脚本在等它的输出,整个流水线的效率就被它拖慢了。我试过用 pandas 优化、用多进程并行、甚至试过 PyPy,效果都不理想。

然后我决定试一下 Rust——不是为了炫技,而是想看看在同样的逻辑下,Rust 到底能快多少。

Rust 版本的设计思路

我并没有一行一行地把 Python 代码翻译成 Rust,而是重新思考了数据的处理方式。Python 版本的瓶颈主要在两个方面:一是逐行读取和解析 CSV 时的开销,二是在内存中维护大量中间数据结构时的内存分配成本。

Rust 版本的核心设计是这样的:使用 csv crate 做流式解析,避免一次性将整个文件加载到内存;使用 rayon 做并行处理,利用多核 CPU 同时处理多个文件;使用 serde 做反序列化,将 CSV 行直接映射到 Rust 的结构体上,类型安全且零开销。

统计计算部分,我没有使用任何高级的统计库,而是用最基础的迭代器和累加器来实现。Rust 的零成本抽象在这里发挥了优势——写起来和高级语言一样简洁,但编译后生成的机器码和手写的 C 代码效率相当。

性能对比

测试环境是一台普通的开发机器:Intel i7-13700K,32GB 内存,NVMe SSD。测试数据是同一批 200 个 CSV 文件,总计约 500 万行。

结果非常直接:Python 版本(pandas)运行时间约 20 分 15 秒,Rust 版本运行时间约 11 秒。性能提升大约是 110 倍。

更让我惊喜的是内存使用:Python 版本的峰值内存占用约 4.2GB(因为 pandas 会将数据完整加载到 DataFrame 中),而 Rust 版本的峰值内存只有约 180MB。这对于在内存受限的环境中运行尤其重要。

Rust 的学习曲线:没有想象中那么陡

很多人被 Rust 的「所有权」和「生命周期」概念吓退。说实话,在写这个脚本的过程中,我确实遇到了一些编译器的「教导」。主要集中在两个地方:

第一个是在处理字符串时。Rust 严格区分 String 和 &str,而在 Python 中你完全不需要考虑这些。一开始我写了很多不必要的 clone() 调用,后来慢慢学会了用引用来传递字符串,代码也变得更高效了。

第二个是在使用 rayon 做并行处理时。并行处理涉及到数据的共享和隔离,Rust 的所有权系统在这里会非常严格地检查你是否正确处理了并发安全。虽然编译错误的信息看起来很吓人,但仔细读编译器的提示,通常都能找到修复的方向。

整个过程我花了大约三天,包括学习 Rust 基础和编写脚本。如果已经熟悉 Rust 的话,这个脚本本身的编写时间可能只需要半天。

什么时候该用 Rust 重写

我并不是说所有 Python 脚本都应该用 Rust 重写。Python 在开发效率、生态丰富度和上手门槛方面的优势依然非常明显。但在以下场景中,Rust 值得认真考虑:

第一,脚本需要频繁运行且执行时间长,时间成本已经成为业务瓶颈。第二,脚本处理大量数据,内存占用成为问题。第三,脚本需要部署到资源受限的环境中(比如小型服务器或边缘设备)。第四,脚本的逻辑相对稳定,不需要频繁修改。

在我的案例中,这四点全部命中,所以用 Rust 重写是一个非常划算的决定。

给想要尝试的 Python 开发者

如果你想尝试用 Rust 解决性能问题,我的建议是:不要从「学完 Rust 再写项目」开始,而是从「带着一个具体问题去学」开始。Rust 的官方教程 The Rust Book 质量非常高,配合 Rustlings 练习项目,大约一周就能掌握基础。然后直接在你的真实场景中实践,遇到问题查文档和问社区,进步会快得多。

正文完
 0
评论(没有评论)