3133X优化单个Rust Clippy lint

Lobsters Hottest 工具

摘要

文章解释了Rust中的`clippy::nonstandard_macro_braces` lint如何被3133X优化,重点在于通过解决编译过程中宏展开的低效问题来提高性能。

<p><a href="https://lobste.rs/s/jtmyg2/optimizing_single_rust_clippy_lint_by">评论</a></p>
查看原文
查看缓存全文

缓存时间: 2026/09/12 22:42

# 将单个Rust Clippy检查规则性能提升3133倍 原文:https://blog.goose.love/posts/making-a-clippy-lint-faster-by-3133x/ 本页面使用**0个Cookie!**(我不确定是否有人阅读,如果有,请通过Mastodon告诉我:https://tech.lgbt/@blyxyas) > 若希望直接查看代码,可访问此链接:https://github.com/rust-lang/rust-clippy/pull/16808 `clippy::nonstandard_macro_braces`是Clippy的一项检查规则,用于捕获使用错误花括号的宏调用。 在Rust中,若要实例化空向量,可调用宏`vec![]`。但技术上并不妨碍你使用`vec!()`,甚至*更离谱*的`vec! {"???"}`。 然而,所有人都讨厌这种写法。没有人喜欢`println! {}`。 因此,Clippy提供了完美的检查规则来避免你成为社区中的新手。正如前文提到的`clippy::nonstandard_macro_braces`(https://rust-lang.github.io/rust-clippy/stable/index.html?search=nonstandard_macro_braces),其中硬编码了常见宏及其惯用花括号格式。 注意,Clippy在**宏展开后**运行。这意味着,例如对于`println! {"..."}`,Clippy实际看到的是: ```rust std::io::_print(std::format_args_nl!("Hello, world!")); ``` 请花一分钟仔细观察。 根据这段代码,花括号信息究竟存储在何处?我相信你很聪明,正因如此你**错了** 我们无法在展开后的检查中得知原始使用了何种花括号。Rust没有明确定义的宏调用映射表,这可能是Clippy面临的最大痛点。 因此我们首先需要确认是否处于宏展开环境中——正如你所料,通过检查是否在宏展开阶段。Rust有时能识别当前代码是否来自宏展开。 现在只剩下一步:沿着展开链逐级回溯: - 若当前处于宏环境: - 向上追溯1层,检查"外部展开数据",即产生当前展开的宏信息 - 若生成者是宏,则获取其名称及对应的惯用花括号格式 接着我们运用源码文本技巧,根据卫生数据中的宏展开信息获取对应代码段。 于是我们得到: ```rust std::io::_print(std::format_args_nl!("Hello, world!")); ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | |- 通过静态"会话全局变量"查询卫生数据,获取代码范围"src/main.rs:1:16 至 src/main:1:32" ``` 通过存储卫生数据的会话全局变量,我们可以定位到"文件src/main.rs,第1行,第16列"至"文件src/main.rs,第1行,第32列"的代码段。该行对应以下字符串: 通过字符串处理(按`!`分割,修剪第二部分并检查首字符是否为`[`、`(`或`{`),我们终于识别出花括号类型! 这意味着我们:调用了两次卫生数据函数,锁定了符号互斥锁(将代码标识符转换为字符串形式以便比较的系统),并进入了递归循环。 **针对每个表达式**。 你的怀疑**完全正确**,我们确实在为整个代码库中的每个表达式、语句**和**项目调用所有这些代码。 这意味着我们为代码中**几乎每个逻辑单元**调用会话全局变量(实际阻塞编译器其他所有操作)**同时**锁定符号互斥锁(拖慢编译器其他所有操作)。 需要明确说明:这并非某个贡献者的错误操作或恶意行为。**责备贡献者绝非正确做法**。 **责任在我们维护者身上**。我们的职责是让写出糟糕代码变得尽可能困难。 Rust已为我们处理了困难部分:无需考虑缓冲区溢出或释放后使用错误(除非进行特殊操作,否则这些问题不会频繁出现)。 是的,这有时意味着编写更多代码。是的,这意味着不能过度使用宏。更意味着不能抽象到失控的程度——否则某个看似无害的函数(作为贡献者你可能已见过上百次)可能占据Clippy 25%的运行时间。 作为开源维护者,我们有三项职责: 1. 抱怨功能需求(这是最重要的) 2. 不干扰用户工作流(神圣不可侵犯) 3. 减少缺陷 显然,如果代码出现问题,说明我们未能尽职。 修复方案?代码量不足200行。我只需将问题函数从展开后检查改写为展开前检查。 不再需要获取源码文本并通过*数学计算*确定括号范围。不再锁定符号互斥锁,不再锁定会话全局变量。 唉,我知道——如果你了解Clippy就会知道:检查规则通常在展开后运行。展开前代码不可信,它会误导你。但这确实是个临时方案,不过它为计算资源节省了数十万美元。 ## 最小化未来性能问题 Clippy现在拥有性能基准测试服务器。没错,我近4年来一直尝试构建这样一个系统,如今终于完成。感谢Rust基金会的合同支持,我能够极大扩展工作规模。现在我们拥有可存储200天数据的基准测试服务器。 系统完全自主托管(至少部分如此),并且采用本地基准测试,因此测试数据应非常接近真实用户体验(我使用的CPU架构十分常见)。 如想了解部署详情,请发送邮件。 --- 感谢阅读,下次Clippy实现突破性改进时再见(或者可能是其他方面的进步...)

相似文章

如何在2026年7月加速Rust编译器

Lobsters Hottest

Nicholas Nethercote报道了Rust编译器近期性能改进,包括平均墙钟时间总体减少5.59%,rustdoc大幅加速总计28%,以及通过PR和PGO训练更改实现的显著Clippy优化。

我是如何在一周内让Rustdoc快33%的

Lobsters Hottest

一位Rustdoc团队成员通过一系列优化和错误修复,在Rustdoc中实现了33%的性能提升,解决了影响文档生成的递归限制问题。

优化 #[sqlx::test] 的重建时间

Lobsters Hottest

一位 Rust 开发者对 SQLx 测试的增量重建时间进行了性能分析和优化,识别了调试信息生成和过程宏开销等瓶颈,并提出了加速测试编译的改进方案。

用 Rust 重写

Hacker News Top

本文评估了2026年的‘Rewrite It In Rust’运动,讨论了现实世界中的性能提升、诸如新错误和平台支持等挑战,并提倡增量重写而非完全重写。