![](http://static.javashuo.com/static/loading.gif)
點擊上方藍字關注咱們html
理清頭腦混沌,覺醒心智天地html5
Mozilla 工程師 Nicholas 總結了他本身迄今爲止爲提高 Rust 編譯器的編譯速度而做的改進(Pull Request)。linux
咱們能夠從他所作的貢獻中,對 Rust 編譯器的編譯細節有所瞭解。由於 rustc 也是 Rust 實現的,因此也能從中學習一些編寫高性能 Rust 代碼的經驗。web
注:本文並不是完整翻譯,只是重點摘錄,以及針對其中的某些內容進行了一些內容擴展。算法
原文地址(點擊閱讀原文可達):後端
https://blog.mozilla.org/nnethercote/2020/04/24/how-to-speed-up-the-rust-compiler-in-2020/緩存
#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 的使用。app
做者爲了這個 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是新事物,它不是大多數操做系統的默認設置,若是咱們在更多地方使用它,幾乎能夠確定會出現隨機的複合錯誤。
整體而言,還不錯。綠色表明性能提高,而紅色則表示相反。
本文分享自微信公衆號 - 覺學社(WakerGroup)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。