这篇文章主要介绍了如何为Rust编译器提速,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。
#68914 : 增量编译使用「SipHasher128」哈希算法来确定自上一次编译器调用以来更改了哪些代码。此PR极大地改善了从输入字节流中提取字节的过程(通过反复进行来确保它在big-endian和little-endian平台上均可工作),在大多数情况下,编译速度最多可提升13%。
在该 PR 中,Nicholas 使用一种简单的移位算法,来替代之前的缓慢算法,带来的好处是,代码量更小,消除了很多 unsafe 代码,性能也提升了。在代码的 Review过程中,还讨论了大小端字节序对哈希算法的影响。而 Rust 的 CI 跑在 ARM、x86 和 WASM 上运行测试,没有大端(big-endian)平台。但通常来说, 对于不同的 CPU 架构,Rust 默认会用对应的主机字节次序存储整形数据,而提升性能。所以,最后的讨论结果是,默认按小端序实现正确,然后留下了注释,在大端序调用相关函数的时候,需要调用方转换字节序。
#69050 :Rust 的 crate 中存储元数据(metadata)广泛使用 LEB 128 编码。但是Rustc 对其编解码的速度还不够快,这个 PR 就是减少了编解码过程中的循环次数,从而提升了性能。并且还消除了一个 Unsafe 的使用。
作者为了这个 PR ,通过使用Callgrind进行性能分析,作者发现 clap-rs-Check-CleanIncr 是受 LEB128 编码影响最大的基准测试+运行+构建组合。先后尝试了 18 种不同的方法进行分析,并且其中有 10 种方法都有性能改进效果。最终选择了现在的改进方法。
可想而知,要写出性能极致的 Rust 代码,还需要耐心且科学地分析才能做到。
BitCode 是 LLVM 引入的一种中间代码,它是源码被编译为二进制机器码过程中的中间形态,也就是说,它既不是源码,也不是机器码。
LLVM 在编译过程中会对代码进行优化,这个优化就是基于BitCode来做。对 BitCode 进行各种类型优化,进行某种逻辑等价的交换,从而使得代码执行效率更高,体积更小。
关于 BitCode 更多介绍,可以查看这篇文章:https://xelz.info/blog/2018/11/24/all-you-need-to-know-about-bitcode/
Rust 在 rlib 和 dylib 中会存储 LLVM BitCode,以便 Rustc 能执行 跨 crate LTO(链接时优化)。
去年,作者从 Rust 的配置文件中注意到 rustc 花了一些时间来压缩它生成的LLVM BitCode,尤其是在 Debug 模式下。于是作者尝试将其更改为不去压缩 BitCode,这样可以加快一些速度,但也显着增加了磁盘上已编译工件的大小。
然后 Alex Crichton (官方人员)告诉作者一些重要的事情:编译器总会为 crate 生成目标代码和 BitCode。正常编译时使用目标代码,而通过链接时间优化(LTO)进行编译时则使用BitCode。用户只能同时而选一,因此生成两种代码通常浪费时间和磁盘空间。
于是作者发了一个 RR #66961,希望从 rlib 中不要存储 LLVM BitCode ,否则会导致增量编译的缓存过大。然而这引起了广泛的讨论,经历了七八个PR 重构之后,最终在 #71323 解决了此问题。
在 Debug 模式下,性能提升了 18% ,rlibs 磁盘占用缩减了 15% 到 20%。如果没有用 Cargo 而直接使用 rustc,则需要加 -Cbitcode-in-rlib=no
才能应用该特性。
#67079: 改进用于热调用模式(hot calling pattern)的 shallow_resolved 函数,性能提升 2%。
#67340: 缩减 Nonterminal 字符(一般可认为是变量,可被替换的符号)大小(到40字节),在构建 serde_derive 的时候大量降低了 memcpy 的调用。性能提升 2% 。
#68694: 减少了InferCtxt中对 RefCell结构的借用,性能提升 5%。
#68848: 编译器的宏解析代码包含一个循环,该循环在每次迭代时实例化一个大型的(Parser类型的)复杂值,但是这些迭代中的大多数并没有修改该值。此PR更改了代码,因此它在循环外初始化了一个解析器值,然后使用Cow避免 Clone 它(修改迭代除外),从而使html5ever基准测试速度提高了15%。(比较有意思的是, 作者说他经常用 Cow,但是他从来却记不住关于 CoW 的使用细节,每次只能去翻文档。。
将 LLD (LLVM 4.0 引入的)作为链接器,可以将链接的时间成倍地提升。然而, issues 39915 报告了一个 Bug,导致至今 LLD 都无法成为 rustc 的默认链接器。
LLD 的特色:
交叉编译非常友好(重点在于嵌入式目标)。
速度非常快。对于增量编译来说,链接时间会占编译时间的一大部分,因此能把这个时间减半相当重要。
当前 Rust 和 LLD 的状态:
Rust 以二进制文件发布了一个 lld 的副本,rust-lld,可以用于大多数平台
rust-lld 默认以 裸机(bare metal)为目标
rust-lld 默认用于 wasm
可以使用“ -C linker-flavor”明确要求使用 rust-lld
在其他地方(Linux/ Mac/ Windows)使用 LLD 的问题:
lld 的 macOS 后端崩溃了,虽然已经开始重写,但还太早期
在linux / unix平台上,不应直接调用ld / lld。而应该通过系统c编译器(即gcc)来调用链接器,链接器的职责是发现像crt1.o这样的系统符号并将其提供给ld。这意味着不能“仅仅”使用rust-lld,而必须将其输入gcc / clang 等等。
Windows-msvc显然还可以,并且似乎在后端使用rust-lld的支持有限,但是Rust 官方还不清楚在这里需要做什么。
Windows-mingw似乎与linux / unix大致类似,除了可能会得到一个古老的GCC,而且事情有些古怪,因为伪Windows-Linux并不是经过严格测试的配置?
更一般地来说,lld是新事物,它不是大多数操作系统的默认设置,如果我们在更多地方使用它,几乎可以肯定会出现随机的复合错误。
感谢你能够认真阅读完这篇文章,希望小编分享的“如何为Rust编译器提速”这篇文章对大家有帮助,同时也希望大家多多支持亿速云,关注亿速云行业资讯频道,更多相关知识等着你来学习!
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。