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