前言
Rust 已經悄然成為了最受歡迎的編程語言之一。作為一門新興底層系統語言,Rust 擁有著內存安全性機制、接近于 C/C++ 語言的性能優勢、出色的開發者社區和體驗出色的文檔、工具鏈和IDE 等諸多特點。本文將介紹筆者使用 Rust 重寫項目并逐步落地生產環境的過程,以及在重寫過程選擇 Rust 的原因、遇到的問題以及使用 Rust 重寫帶來的成果。
我們目前正在使用 Rust 開發的項目叫做 KCL,目前全部實現代碼已經在 Github 上開源。KCL 是一個基于約束的記錄及函數領域編程語言,致力于通過成熟的編程語言技術和實踐來改進特領域如云原生 Kubernetes 領域的大量繁雜配置編寫和安全策略校驗等,致力于構建圍繞配置的更好的模塊化、擴展性和穩定性,更簡單的邏輯編寫,以及更快的自動化集成和良好的生態延展性。更具體的 KCL 使用場景請訪問 KCL 網站,本文中不再過多贅述。
KCL 之前是使用 Python 編寫的,出于用戶使用體驗、性能和穩定性的考慮,決定用 Rust 語言進行重寫,并獲得了以下好處:
1.更少的 Bug,源于 Rust 強大的編譯檢查和錯誤處理方式
2. 語言端到端編譯執行性能提升了66%
3. 語言前端解析器性能提升了20 倍
4. 語言中端語義分析器性能提升了40 倍
5. 語言編譯器編譯過程平均內存使用量變為原來 Python 版本的一半
我們遇到了什么問題
就像社區中同類型項目 deno, swc, turbopack, rustc 等編譯器、構建系統或者運行時在技術上使用 Rust 做的事情類似,我們使用 Rust 完整構建了編譯器的前中端和運行時,取得了一定的階段性成果,但是我們大約在一年前并不是這個樣子的。
一年前,我們使用 Python 語言構建了整個 KCL 語言編譯器的實現,雖然在一開始的時候運行良好,Python 簡單易上手,生態豐富,團隊的研發效率也很高,但是隨著代碼庫的擴張和工程師人數的增加,代碼維護起來愈加困難。
盡管我們在項目中強制編寫 Python 類型注解,采用更嚴格的 lint 工具,代碼測試行覆蓋率也達到了 90% 以上,但是仍然會出現很多諸如 Python None 空對象,屬性未找到等運行時才會出現錯誤,并且重構 Python 代碼時也需要小心翼翼,反應到 KCL 語言上就是一個接一個的 bug, 嚴重影響用戶使用體驗。
此外,當 KCL 使用對象是廣大開發者用戶時,編程語言或者說編譯器內部實現出現任何錯誤都是不可容忍的,這些也給我們的用戶使用體驗帶來了一系列問題,使用 Python 編寫的程序啟動速度較慢,性能無法滿足自動化系統在線編譯和執行的效率訴求,因為在我們的場景中,用戶修改 KCL 代碼后需要能很快的展示編譯結果,顯然使用 Python 編寫的編譯器并不能很好地滿足使用需求。
為什么選擇 Rust
筆者所在團隊基于如下原因選擇了 Rust
1. 使用 Go, Python, Rust 三種語言實現了簡單的編程語言棧式虛擬機并作了性能對比,Go 和 Rust 在這個場景下性能接近,Python 有較大性能差距,綜合考慮下采用了 Rust,具體三種語言實現的棧式虛擬機代碼細節在https://github.com/Peefy/StackMachine,感興趣的同學可以前往瀏覽
2. 越來越多的編程語言的編譯器或運行時特別是前端基礎設施項目采用 Rust 編寫或重構,此外基礎設施層,數據庫、搜索引擎、網絡設施、云原生、UI 層和嵌入式等領域都有 Rust 的出現,至少在編程語言領域實現方面經過了可行性和穩定性驗證
3. 考慮到后續的項目發展會涉及區塊鏈和智能合約方向,而社區中大量的區塊鏈和智能合約項目采用 Rust 編寫
4. 通過 Rust 獲得更好的性能和穩定性,讓系統更容易維護、更加健壯的同時,可以通過 FFI 暴露 C API 供多語言使用和擴展,方便生態擴展與集成
5. Rust 對 WASM 的支持比較友好,社區中大量 WASM 生態是由 Rust 構建,KCL 語言和編譯器可以借助 Rust 編譯到 WASM 并在瀏覽器中運行
基于以上原因綜合考慮選擇了 Rust 而不是 Go 等其他語言,整個重寫過程下來發現 Rust 綜合素質確實過硬(第一梯隊的性能,足夠的抽象程度),雖然在一些語言特性特別是生命周期等上手成本有一些,生態上還不夠豐富。
總之編程語言可以做的事情,Rust 均可以做,具體可能還是要根據具體的場景和問題來做選擇。同時如果想要使用好 Rust, 還需要深入理解內存、堆棧、引用、變量作用域等這些其它高級語言往往不會深入接觸的內容。
使用 Rust 過程中遇到了哪些困難
雖然決定了使用 Rust 重寫整個 KCL 項目,其實團隊成員大部分成員是沒有使用 Rust 編寫一定代碼體量項目的經驗,包括筆者個人自己也僅僅學習過《The Rust Programming Language》中的部分內容,依稀記得學習到Rc和RefCell等智能指針內容就放棄了,那時沒想到 Rust 中還能有與 C++ 中類似的東西。
使用 Rust 前預估的風險主要是 Rust 語言接觸和學習的成本,這個確實在各種 Rust 的文章博客中均有提到,因為 KCL 項目整體架構并未發生太大變化,只是部分模塊設計和代碼編寫針對 Rust 作了優化,因此整個重寫是在邊學邊實踐中進行。確實在剛開始使用 Rust 編寫整個項目的時候花費在知識查詢、編譯排錯的時間還是很多的,不過隨著項目的進行漸入佳境,筆者個人經驗使用 Rust 遇到的困難主要是心智轉換和開發效率兩方面:
心智轉換
首先 Rust 的語法語義很好地吸收和融合了函數式編程中類型系統相關的概念,比如抽象代數類型 ADT 等,并且 Rust 中并無“繼承”等相關概念,如果不能很好地理解甚至連其他語言中稀松平常的結構定義在 Rust 中可能都需要花費不少時間,比如如下的 Python 代碼可能在 Rust 中的定義是這個樣子的
Python
Rust
當然更多的時間是在與 Rust 編譯器本身的報錯作斗爭,Rust 編譯器會經常使開發人員"碰壁",比如借用檢查報錯等,特別是對于編譯器來講,它處理的核心結構是抽象語法樹 AST,這是一個遞歸和嵌套的樹結構,在 Rust 中有時很難兼顧變量可變性與借用檢查的關系,就如 KCL 編譯器作用域Scope的結構定義結構那樣,對于存在循環引用的場景,用于需要顯示意識到數據的相互依賴關系,而大量使用Rc,RefCell和Weak等 Rust 中常用的智能指針結構
開發效率
Rust 的開發效率可以用先抑后揚來形容。在剛開始上手寫項目時,如果團隊成員沒有接觸過函數式編程相關概念以及相關的編程習慣,開發速度將顯著慢于 Python、Go 和 Java 等語言,不過一旦開始熟悉 Rust 標準庫常用的方法、最佳實踐以及常見 Rust 編譯器報錯修改,開發效率將大幅提升,并且原生就能寫出高質量、安全、高效的代碼。
比如筆者個人當初遇到一個如下代碼所示的與生命周期錯誤前前后后排查了很久的時間才發現原來是忘記標注生命參數導致生命周期不匹配。此外 Rust 的生命周期與類型系統、作用域、所有權、借用檢查等概念耦合在一起,導致了較高的理解成本和復雜度,且報錯信息往往不像類型錯誤那么明顯,生命周期不匹配錯誤報錯信息有時也略顯呆板,可能會導致較高的排錯成本,當然熟悉相關概念寫多了之后效率會提高不少。
使用 Rust 重寫收益比
經過團隊幾個人花費幾個月時間使用 Rust 完全重寫并穩定落地生產環境幾個月后,回顧整個過程感覺這件事情的收獲非常大。
從技術角度層面來看,重寫的過程不僅僅鍛煉了快速學習一門新的編程語言、編程知識并將其付諸實踐,并且整個重寫過程讓我們又反思了 KCL 編譯器中設計不合理的部分并進行修改,對一個編程語言而言,這是一個長周期的項目,我們收獲的是編譯器系統更加穩定、安全,且代碼清晰,bug 更少、性能更好的技術產品服務于用戶,雖然沒有全部模塊得到高達 40 倍的性能,因為部分模塊如 KCL 運行時的性能瓶頸在于內存深拷貝操作,但筆者個人認為仍然是值得的。且當 Rust 使用時間到達一定時長后,心智和開發效率不再是限制因素,就像學車那樣,拿到駕照后更多是上路實踐和總結。
結語
筆者個人覺得使用 Rust 重寫項目后最重要的是不是我學會了一門新的編程語言,也不是 Rust 很流行很火因此我們在項目中采用一下,或者使用 Rust 編寫了多少炫技的代碼,是真真正正地使得語言和編譯器本身更加穩定,能夠在生產環境平穩落地并長期使用,啟動速度和自動化效率不再受困擾,性能優于社區其他同類型領域編程語言,使我們語言和工具的用戶感受到體驗提升,這些都得益于 Rust 的無 GC、高性能、更好的錯誤處理內存管理、零抽象等特性。
審核編輯 :李倩
-
編程語言
+關注
關注
10文章
1950瀏覽量
34989 -
編譯器
+關注
關注
1文章
1642瀏覽量
49291 -
Rust
+關注
關注
1文章
230瀏覽量
6668
原文標題:性能提升 40 倍!我們用 Rust 重寫了自己的項目
文章出處:【微信號:Rust語言中文社區,微信公眾號:Rust語言中文社區】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
評論