血燃 Rust
在保持行为不变的前提下,提升测量到的性能。保留能对代表性输入产生可重复增益的最简单变更。
编辑前先测量
- 确定工作负载、目标指标及正确性约束。阅读热点路径及其调用者。尊重项目的工具链、最低支持的 Rust 版本、特性及部署目标。解释请求仅授权测量和分析,不授权编辑。
- 使用项目优化的发布配置(通常为
release)捕获基线。记录输入、精确命令、工具链、标志、CPU 及线程数。使用真实的输入大小和分布,包括小规模及极端不利情况。 - 找出性能瓶颈。根据情况分析 CPU 时间、内存分配、I/O 或锁等待。对于异步阻塞,使用运行时感知的追踪来检查任务调度和队列增长。优先使用现有工具;分析章节列出了常用工具以及如何在发布构建中保留符号。如果无法进行分析,则使用有针对性的测量,并说明证据的局限性。
- 提出假设并做出单一的、可测量的变更。运行有针对性的正确性测试并重复相同的基准测试。在声称有用户可见的改进之前,先在全量工作负载中验证结果。
- 保留有收益的更改。仅撤销不成功的编辑,保留不相关的工作。当达到目标或进一步更改缺乏证据时停止。
使基准测试可信
- 将 setup、编译、输入生成和进程启动与操作本身分开,除非它们属于目标指标。消费结果。对于微基准测试,使用基准库的优化屏障或
std::hint::black_box处理输入和输出。这些屏障是尽力而为的,因此请检查不可思议的结果。 - 比较等价的工作量和输出。保持工具链、配置、特性、分配器、CPU 设置和线程数不变,除非正是要测试的变量。测量稳态时需进行预热。在相关时分别测量冷启动。
- 重复运行,并报告样本数、中位数及变异度。在漂移重要时交替运行基线和候选版本。差异在噪声范围内不算成功。
- 指标与任务相匹配。对于服务,测量指定负载下的 p95/p99 延迟、吞吐量、错误和资源使用。检查延迟降低是否源于工作丢弃或队列增长。
- 重用现有基准测试。当新逻辑需要时,添加一个小的回归测试用例。未经任务授权,不得安装工具、添加依赖或更改构建设置。切勿编造测量结果。
根据证据选择变更
从消除不必要的工作和优化算法开始。只有当剩余瓶颈需要时才考虑更低层次的变更。当测量到的瓶颈需要更多细节时,阅读技术指南中的相应章节。
| 观察到的开销 | 候选变更及检查 |
|---|---|
| 重复扫描或计算 | 改进算法或集合选择。移除重复解析、排序、序列化和查找。如果缓存有帮助,需考虑缓存失效和内存问题。 |
| 分配或复制 | 借用 &str 和切片,移动所有权值,重用缓冲区,并预留可信容量。在替换已分配值时尝试 clone_from。避免中间集合。对于共享句柄,clone 可能开销很小,需测量其成本。 |
| 集合查找 | 使用借用键和 entry API,以避免重复工作。当访问模式适合时,比较密集向量或排序数据。更换哈希器时,保留对不可信键的碰撞攻击抵抗力。 |
| 排序或移除 | 选择时使用 select_nth_unstable,当相等元素顺序无关时使用 sort_unstable,对于高开销的键使用 sort_by_cached_key。仅当元素顺序无关时使用 swap_remove。 |
| 缓存未命中或大值 | 比较结构体数组与数组结构体。检查 size_of、align_of 以及缓存未命中。内联存储可能增加复制成本。考虑对罕见的大枚举负载进行装箱,需权衡分配和间接引用。 |
| 小型热调用或循环 | 优先普通迭代。通过切片、迭代或范围断言帮助编译器移除边界检查。在内联或展开之前检查优化后的汇编。测试空输入、尾部、溢出和代码大小。 |
| 文本搜索 | 重用编译好的正则表达式,仅请求所需的匹配信息。当编码语义允许时,比较专门的字节搜索。避免在热循环内重建搜索状态。 |
| 可向量化计算 | 在显式 SIMD 之前检查自动向量化。检查生成的代码。在需要的地方保护目标特定指令,并保留标量回退。测试尾部和代表性 CPU。保持对齐、边界和数值行为。 |
| 独立 CPU 工作 | 比较串行执行与有界并行。测量调度开销、争用、带宽、线程数和总 CPU 消耗。仅当开销重要时调整任务大小。保持归约语义。 |
| I/O 或异步阻塞 | 重用行缓冲区,批量处理小 I/O,并处理冲刷错误。保持延迟、换行处理和持久性。将阻塞或高 CPU 工作移出异步 worker。限制并发和队列。保持排序、背压和取消行为。 |
| 构建或代码生成限制 | 比较现有设置与 thin LTO。在组合之前分别测试 fat LTO 和 codegen-units = 1。包含构建时间和二进制大小。仅当部署硬件允许时才使用特定 CPU 标志。 |
保持正确性
保持公共 API、错误处理、验证、排序、序列化和 FFI 契约不变。保留整数溢出和浮点行为。枚举表示或变体顺序可能更改布局和判别式;两者都不保证枚举更小或更快。
优先使用安全 Rust 和现有依赖。引入不安全代码需要测量到的需求、明确的安全不变量以及有针对性的测试。在支持且相关时使用 Miri 或 sanitizers。切勿为了基准测试速度而牺牲验证或健全性,也不要将通过的测试视为健全性的证明。
报告结果
说明瓶颈、变更及保留/撤销决定。包含可复现的命令、前后结果及其变异度、相关的分配或内存数据、正确性检查以及权衡。区分多核墙钟增益与单核效率。说明未测量的声称及未经检查的平台。