关于 rust-performance
面向 Rust 编码 Agent 的性能分析与优化技能,强调先测量再修改。内容涵盖基准比较、分配与集合选择,以及编译器设置的权衡。
主要功能
- 01优化前先进行性能分析
- 02介绍 Criterion 和 divan 基准测试
- 03讲解内存分配复用模式
- 04按访问模式选择集合
- 05说明哈希和代码生成的权衡
适用场景
- 01定位 Rust 性能瓶颈
- 02依据基准基线比较改动
- 03减少热点路径中的内存分配
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ rewrite-rs/ skills --skill rust-performance安装后在 Claude Code 中输入/rust-performance确认已加载。
附带文件agents/openai.yamlALLOCATION.md
详细介绍
Rust 性能
这里的每项改动都必须有明确的测量依据:性能分析是入场券,
代码生成标志只贡献最后 5%,而不是第一步。异步吞吐量见 /async-rust;用
unsafe 提速见 /unsafe-rust。
先测量,再改动
任何优化都必须有明确指出它的测量依据。跳过这一步要付出双重代价: 一是改动没有带来任何提速,二是改动让代码变差,却什么也没换来。性能分析会找出 真正耗时的那个函数——几乎从来不是读代码时看起来开销最大的那个。
有意义的基准测试
用 criterion 与已存储的基线进行统计比较;若轻量级测试框架就够用,则用
divan。对操作本身进行基准测试,而不是测试其外围的准备工作。
没有它,性能分析器显示的将是地址,而不是函数名:
[profile.bench]
debug = 1
在热调节限频的笔记本电脑上运行基准测试,得到的数字虽然有意义,但无法比较—— 进行对比的测试运行之间,机器必须保持空闲。
答案通常是减少分配
这是收益最高的类别,也是性能分析最常指出的问题:
- 已知大小时使用
Vec::with_capacity——默认行为会扩容并复制。 - 清空并复用,而不是每次迭代都分配:在外部创建缓冲区,在内部调用
clear()。 - 对永远不会扩容的拥有型序列,使用
Box<[T]>和Box<str>。 - 使用
mem::take从&mut中移出值,而不进行克隆。 - 热循环中不要使用
format!——每次调用都会分配一个新的String。 - 对可借用或拥有的情况使用
Cow——仅在值发生变化时复制。
一个完整示例:使用已知的总大小进行拼接:
fn joined(parts: &[String]) -> String {
let total = parts.iter().map(|p| p.len() + 1).sum();
let mut out = String::with_capacity(total);
for (i, part) in parts.iter().enumerate() {
if i > 0 {
out.push(' ');
}
out.push_str(part);
}
out
}
复用模式和传递缓冲区的写法见 ALLOCATION.md。
根据访问模式选择集合
对于较小的 n,Vec 比 HashMap 更快,原因是缓存行为,而不是渐近复杂度。
需要迭代顺序时使用 BTreeMap。若只构建一次、读取多次,则使用排序后的 Vec 加
binary_search。临界点需要测量,而不是背下一个固定值。
哈希
默认哈希器可抵御 DoS,因此速度相应较慢。只有键不受攻击者控制时,替换它才安全且 值得——例如内部 ID、枚举判别值、驻留字符串。若键由攻击者控制,这就会成为 拒绝服务攻击面,再快的查找也弥补不了这一点。应明确说明风险,而不是把替换说成 毫无代价的收益。
迭代器与边界检查
迭代器链会向优化器提供索引循环所隐藏的长度不变量,这正是边界检查能够消失的原因:
同一循环中的两次 [i] 读取可以改为 iter().zip(),优化器便能证明访问不会越界。
这里是惯用写法也能带来速度的一个例子——没有任何取舍需要争论。
最后 5%
LTO、codegen-units = 1、PGO、target-cpu。根据 ADR 0007:这些是应用层决策,
绝不能设为库的默认值;每项都应同时说明可移植性代价与收益——调整参数只需一行,
性能分析却要花一个下午:
- 在提交的配置中使用
target-cpu=native,会生成无法在较旧机器上运行的二进制文件。 - PGO 需要有代表性的工作负载,否则优化的将是错误的执行路径。
- LTO 和
codegen-units = 1带来的只是百分比级提升,而非倍数级提升,代价是更长的构建时间。
panic = "abort" 不是性能默认设置:这是应用层的选择,库不能假定会进行栈展开,
而且它会改变 #[should_panic] 下的行为。
延后处理
异步吞吐量、执行器停顿和 spawn_blocking 属于 /async-rust——这是运行时问题,
而不是吞吐量问题。若要使用 unsafe 跳过检查,请看 /unsafe-rust;那里的答案是
该代码块需要有健全性论证,而不只是基准测试。性能分析找出的克隆操作是否根本不该
存在,见 /ownership-not-clone。
验证
cargo bench # against the recorded baseline where one exists
cargo clippy --all-targets # at the level the repo configures; add -- -D warnings only if it configures none
cargo test --release
没有存储基准的基准测试无法证明任何变更有效——先记录变更前的数据,再测量变更后的数据,并与基准测试报告的波动范围进行比较。Clippy 会保留目标仓库配置的 lint 级别;对于完全未配置 lint 的仓库,-D warnings 是备用方案。