原文标题: Cheap tricks for high-performance Rust
原文链接: https:// deterministic.space/hig h-performance-rust.html
你正在写Rust但是它却不够快?即使你已经执行了cargo build --release
? 这里是一些实用的小技巧用于提升Rust项目的运行时速度而不用修改任何代码。
请记住下面建议但是不要替换掉掉实际性能分析和优化。我也认为能够在实际使用中表现出的基准测试(benchmarks)是唯一有效的方式。
调整我们的release
profile
首先让我们在执行cargo build --release
的时候开启一些优化选项。处理很简单: 我们开启一些功能,这些功能会让发布构建更慢,但是能够得到更加彻底的优化。
我们添加下面的标记到我们的主Cargo.toml
文件里,也就是cargo工作目录下最上层的Cargo.toml
.如果你的文件里没有profile.release
这部分,手动添加:
[profile.release]
链接时优化
接下来我们要做的第一件事就是开启[链接时优化](link-time optimization) (LTO)。这是一种整个程序或者内部模块优化因为它是链接不同部分到你的二进制(程序)的最后一步。你可以把它想象为跨越不同依赖边界时允许更好的内联(当然这也会更加复杂)。
Rust可以使用多种链接器风格(flavors),其中我们想要的一种是"贯穿所有crates优化(optimize across all crates)",也被叫"胖(fat)"。要设置这个选项,需要在你的profile里添加lto
标记:
lto = "fat"
代码生成单元
接下来是一个相似的主题。为了加速编译时间,Rust尝试把你的crates分割成小块然后尽可能地并行编译。这样做的缺点就是编译器在这些块至今能够优化的地方就少了。所以,让我们告诉编译器每次把一crate作为一个块:
codegen-units = 1
设置指定目标CPU
默认情况下,Rust构建二进制文件会希望它能在尽可能多的体系结构(译者注:如x86,arm)上运行。但是,你可能实际上有一个带有很酷的新特性的新型CPU!要启用这些(译者注: 即针对你的CPU进行优化),我们可以添加:
-C target-cpu=native
作为一个Rust标记,也就是环境变量中的RUSTFLAGS
或者在你的.cargo/config
文件中关于目标(target)的rustflags
中止(Aborting)
现在,我们开始接触到一些更加不安全的选项。记住,Rust默认怎样使用栈展开(stack unwinding)(在大多数常见平台)?是牺牲了性能!让我们跳过栈追踪和捕捉panic的功能来减少代码大小和得到更好的缓存利用:
panic = "abort"
请记住,一些库可能依赖于栈展开并且在你开启这个选项后会发生很糟糕的崩溃!
使用不同的分配器
Rust程序常做的一件事是分配内存。并且,它们只是自己来做这件事而是实际上使用一个(外部)库:一个分配器。当前Rust二进制程序默认使用系统分配器,以前它们在自己的标准库包含了自己的分配器。(这个改动带来了更小的二进制程序和更好的调试能力从而让一部分感到十分高兴)。
尽管有时候你的系统分配器不是最好的方案。不用担心,我们可以更换它,我建议jemaloc
和mimalloc
都可以尝试一下。
jemalloc
jemalloc是Rust先前打包的分配器并且Rust编译期仍在使用它。它致力于减少内存段和支持高并发。也是FreeBSD的默认默认分配器。如果这个听起来比较吸引你,让我们来试一下!
开始时,添加jemallocator
crate作为依赖:
[dependencies]
jemallocator = "0.3.2"
然后在你的程序入口(main.rs
),把它设置为全局分配器,就像下面这样:
#[global_allocator]
static GLOBAL: jemallocator::Jemalloc = jemallocator::Jemalloc;
请注意jemalloc并不是所有的平台都支持。
mimalloc
另一个有趣的分配器是mimalloc
。它由微软开发,体积小,并且有一些对自由列表的创新。
它的功能里包含有一些可以配置的安全特性(看一下它的Cargo.toml
)。这意味着我们可以关闭这些功能以获取更好的性能!然后添加mimalloc
crate 作为一个依赖,就像下面这样:
[dependencies]
mimalloc = { version = "0.1.17", default-features = false }
和上面一样,在程序入口处添加下面的代码:
#[global_allocator]
static GLOBAL: mimalloc::MiMalloc = mimalloc::MiMalloc;
Profile Guided Optimization
这是LLVM里一个整洁的特性,但是我从来没有使用过。请阅读相关文档
实际分析和优化你的代码
现在,这里是需要你去调整代码然后修正所有的clone()
调用。不幸的是,这是另一篇文章的主题!(等我一年时间来写这篇文章,在此期间,你可以阅读 cows!)
编辑:有人一直想要一些关于如何优化Rust代码的建议,幸运的是,下面有一些链接:
- 非常方便的
cargo flamegraph
(也是一个独立的工具) - Christopher Sebastian最近发布的文章:[How To Write Fast Rust Code]
- Robin Freyler在RustFest 2018的演讲:Fastware Workshop
感谢阅读!
由于译者水平有限,翻译不到之处还请见谅,欢迎提出改进意见!
欢迎关注本人微信公众号: