黄色网页视频 I 影音先锋日日狠狠久久 I 秋霞午夜毛片 I 秋霞一二三区 I 国产成人片无码视频 I 国产 精品 自在自线 I av免费观看网站 I 日本精品久久久久中文字幕5 I 91看视频 I 看全色黄大色黄女片18 I 精品不卡一区 I 亚洲最新精品 I 欧美 激情 在线 I 人妻少妇精品久久 I 国产99视频精品免费专区 I 欧美影院 I 欧美精品在欧美一区二区少妇 I av大片网站 I 国产精品黄色片 I 888久久 I 狠狠干最新 I 看看黄色一级片 I 黄色精品久久 I 三级av在线 I 69色综合 I 国产日韩欧美91 I 亚洲精品偷拍 I 激情小说亚洲图片 I 久久国产视频精品 I 国产综合精品一区二区三区 I 色婷婷国产 I 最新成人av在线 I 国产私拍精品 I 日韩成人影音 I 日日夜夜天天综合

C++、Python、Rust、Scala 構(gòu)建編譯器的差異性究竟有多大?

系統(tǒng) 2113 0

640?wx_fmt=gif

【CSDN 編者按】編程語(yǔ)言之爭(zhēng)是開(kāi)發(fā)者們熱議的永恒話題,在不同語(yǔ)言的選擇和設(shè)計(jì)決定上也都觀點(diǎn)不一。那么在面對(duì)大型項(xiàng)目時(shí)該如何選擇具體實(shí)現(xiàn)呢?本文的作者借課程項(xiàng)目之機(jī),比較了Rust、Haskell、OCaml、C++、Python、Scala 等語(yǔ)言編寫(xiě)的編譯器差異,最終發(fā)現(xiàn),這些語(yǔ)言在代碼量和功能實(shí)現(xiàn)上簡(jiǎn)直千差萬(wàn)別!

C++、Python、Rust、Scala 構(gòu)建編譯器的差異性究竟有多大?_第1張圖片

作者 |?Tristan Hume

譯者 |?彎月,責(zé)編 | 郭芮

出品 | CSDN(ID:CSDNnews)

以下為譯文:

我在滑鐵盧大學(xué)的最后一個(gè)學(xué)期選了CS444:編譯原理這門(mén)課程,課程項(xiàng)目是編寫(xiě)一個(gè)編譯器,將Java語(yǔ)言的子集編譯成x86代碼,三人結(jié)組,語(yǔ)言自由選擇。

?

這是個(gè)難得的機(jī)會(huì),我可以在同樣的大型項(xiàng)目下比較不同的實(shí)現(xiàn),而且我的朋友們的水平也跟我很相近,所以我可以借這個(gè)機(jī)會(huì)看看不同的設(shè)計(jì)和語(yǔ)言選擇。我從這個(gè)項(xiàng)目中獲得了不少心得,盡管這個(gè)比較并不完美,但比那些僅靠個(gè)人觀點(diǎn)來(lái)比較編程語(yǔ)言的人要好多了。

?

我們的編譯器是用Rust寫(xiě)成的,首先與另一個(gè)使用了Haskell的組進(jìn)行了比較。我認(rèn)為他們的編譯器應(yīng)該更簡(jiǎn)潔,但實(shí)際的代碼行數(shù)差不多。與另一個(gè)使用了OCaml的團(tuán)隊(duì)的比較也得到了同樣的結(jié)果。然后我與一個(gè)使用了C++的團(tuán)隊(duì)比較,結(jié)果如我預(yù)料的那樣,由于有頭文件,以及缺乏匯總類(lèi)型和模式匹配的支持,導(dǎo)致他們的編譯器大了30%。下一個(gè)是跟我一個(gè)朋友的Python實(shí)現(xiàn)進(jìn)行的比較,他的代碼量不到我們的一半,這要?dú)w功于元編程和動(dòng)態(tài)類(lèi)型。另一個(gè)朋友的團(tuán)隊(duì)使用了Scala,實(shí)現(xiàn)的編譯器代碼量也小于我們。最讓我驚訝的比較就是與另一個(gè)同樣使用Rust的團(tuán)隊(duì)的比較,他們的代碼量是我們的三倍,因?yàn)樗麄儾捎昧瞬煌脑O(shè)計(jì)決定,這最終導(dǎo)致了同樣的功能需要的代碼量產(chǎn)生了巨大差異!

?

本文中首先我會(huì)來(lái)解釋一下此次比較的意義,介紹各個(gè)項(xiàng)目的基本情況,然后再解釋引發(fā)編譯器大小差異的部分原因。最后,我會(huì)談一談從各個(gè)比較中學(xué)到的東西。

?

?

640?wx_fmt=png

比較的意義

?

?

你也許會(huì)認(rèn)為,代碼行數(shù)(我同時(shí)比較了代碼行數(shù)和字節(jié)數(shù))是個(gè)很糟糕的度量,但我認(rèn)為在這個(gè)項(xiàng)目中這種度量可以給出很有用的信息。在我看來(lái),至少代碼行數(shù)是各個(gè)不同的團(tuán)隊(duì)在同一個(gè)大型項(xiàng)目上工作時(shí)最可控的一個(gè)變數(shù)。

?

  • 直到我們的項(xiàng)目完成之前,沒(méi)有任何人(包括我)知道我會(huì)統(tǒng)計(jì)代碼行數(shù),所以沒(méi)有人在行數(shù)度量上做手腳,每個(gè)人都盡最大努力來(lái)快速、正確地完成項(xiàng)目。每個(gè)人(除了后面我會(huì)談到的使用Python的項(xiàng)目之外)都在實(shí)現(xiàn)同一個(gè)程序,目的只有一個(gè),就是在同樣的截止日期之前通過(guò)同樣的自動(dòng)化測(cè)試套件,所以也不會(huì)有某個(gè)組試圖解決不同的問(wèn)題,或者解決更難的問(wèn)題的情況。每個(gè)組都在這個(gè)項(xiàng)目上花了數(shù)月的時(shí)間,大家都在逐步地添加功能,從而通過(guò)已知和未知的測(cè)試。這意味著代碼整潔易讀,沒(méi)有任何取巧的地方。

?

  • 除了要通過(guò)的課程測(cè)試之外,代碼不會(huì)被用于任何其他用途,也沒(méi)人會(huì)閱讀它,而且由于它只能編譯Java語(yǔ)言的一個(gè)子集,所以它也沒(méi)有任何其他用途。除了標(biāo)準(zhǔn)庫(kù)之外也不允許使用任何庫(kù),甚至連輔助解析的庫(kù)都不允許(如果標(biāo)準(zhǔn)庫(kù)中沒(méi)有包含此功能的話)。這意味著也不會(huì)出現(xiàn)任何僅有部分團(tuán)隊(duì)使用的、強(qiáng)大的編譯器庫(kù)來(lái)干擾比較。

?

  • 在最終的提交截止日期之后,會(huì)運(yùn)行一次秘密的測(cè)試(我們看不到該測(cè)試),也就是說(shuō),自己編寫(xiě)測(cè)試用例并測(cè)試代碼,可以保證編譯器的健壯、正確,也可以處理邊界情況。

?

  • 盡管參與的每個(gè)人都是學(xué)生,但我討論的這些團(tuán)隊(duì)都是我認(rèn)為非常優(yōu)秀的程序員。每個(gè)人都至少有兩年全職的實(shí)習(xí)經(jīng)驗(yàn),大多數(shù)都在高端的科技公司,一些公司甚至還在開(kāi)發(fā)編譯器。幾乎每個(gè)人都有7-13年的編程經(jīng)驗(yàn),都十分熱衷在網(wǎng)上閱讀課程之外的東西。

?

  • 自動(dòng)生成的代碼沒(méi)有統(tǒng)計(jì)在內(nèi),但生成的語(yǔ)法文件和代碼被統(tǒng)計(jì)了。

?

因此我認(rèn)為,就各個(gè)項(xiàng)目需要花費(fèi)的精力,以及如果是長(zhǎng)期項(xiàng)目的話需要花費(fèi)多少精力去維護(hù)而言,代碼量是一個(gè)很不錯(cuò)的近似統(tǒng)計(jì)。我認(rèn)為,微小的差異也能反映出巨大的問(wèn)題,比如上面說(shuō)過(guò)的用Haskell編寫(xiě)的編譯器代碼量不到C++的一半。

?

?

640?wx_fmt=png

Rust(比較基準(zhǔn))

?

?

我和團(tuán)隊(duì)里的另一名成員以前分別寫(xiě)過(guò)1萬(wàn)多行的Rust代碼,另一個(gè)成員在某次編程馬拉松項(xiàng)目上寫(xiě)過(guò)大約500行Rust。我們的編譯器用wc -l統(tǒng)計(jì)的結(jié)果是6806行,其中包括5900代碼行(不包括空行和注釋?zhuān)瑆c -c的結(jié)果為220kb。

?

我發(fā)現(xiàn)的一個(gè)問(wèn)題是,這幾項(xiàng)度量的比例在其他項(xiàng)目中也是相似的,只有一些微小的差異(過(guò)會(huì)兒我會(huì)介紹)。下文中提到代碼行數(shù)時(shí),我指的都是wc -l的結(jié)果,但上述結(jié)論表明,代碼行數(shù)按照哪個(gè)規(guī)則進(jìn)行統(tǒng)計(jì)其實(shí)是無(wú)所謂的(除非特別指出),你可以通過(guò)比例進(jìn)行換算。

?

我寫(xiě)過(guò)另一篇關(guān)于設(shè)計(jì)的文章(http://thume.ca/2019/04/18/writing-a-compiler-in-rust/),這個(gè)設(shè)計(jì)通過(guò)了所有公開(kāi)和秘密的測(cè)試。它還包括幾個(gè)額外的特性,這些特性我們僅僅是出于興趣而開(kāi)發(fā),并沒(méi)有想著通過(guò)測(cè)試。這些特性大概占用了400行。我們總共的單元測(cè)試和測(cè)試用的代碼大約占了500行。

?

?

640?wx_fmt=png

Haskell

?

?

Haskell團(tuán)隊(duì)由我的兩個(gè)朋友組成,他們每個(gè)人大概寫(xiě)過(guò)幾千行Haskel,還閱讀過(guò)許多網(wǎng)上的Haskell內(nèi)容,以及許多其他類(lèi)似的語(yǔ)言,如OCaml和Lean。他們還有另一個(gè)我不太熟悉的團(tuán)隊(duì)成員,但似乎是個(gè)很厲害的程序員,以前也用過(guò)Haskell。

?

他們編譯器的wc -l結(jié)果是9750行,357kb,7777 SLOC(源代碼行數(shù))。這個(gè)團(tuán)隊(duì)的度量比例的差別也最大,他們的編譯器中行數(shù)為1.4倍,SLOC為1.3倍,字節(jié)數(shù)為1.6倍。他們并沒(méi)有實(shí)現(xiàn)任何額外功能,但通過(guò)了所有公開(kāi)和秘密的測(cè)試用例。

?

需要指出的重要的一點(diǎn)是,只有把測(cè)試用例統(tǒng)計(jì)在內(nèi),對(duì)這個(gè)團(tuán)隊(duì)才公平,因?yàn)樗麄兊拇a是最正確的,包含了1600行測(cè)試用例,并且捕獲了好幾個(gè)團(tuán)隊(duì)未能捕獲的邊界情況,只不過(guò)是課程提供的測(cè)試用例沒(méi)有覆蓋到這些邊界情況而已。所以,如果兩者都不統(tǒng)計(jì)測(cè)試用例的話,他們的代碼是8.1k行,我們的是6.3k行,僅是我們的1.3倍。

?

為了讓度量更合理,我還統(tǒng)計(jì)了字節(jié)數(shù),因?yàn)镠askell項(xiàng)目平均每行要更長(zhǎng),而且沒(méi)有許多只有結(jié)束括號(hào)的行,它的單行函數(shù)也不會(huì)被rustfmt分解成多行。

?

在與團(tuán)隊(duì)里的另一個(gè)朋友深入挖掘了代碼大小的問(wèn)題后,我們找到了以下理由來(lái)解釋代碼大小的差異:

?

  • 我們采用了手寫(xiě)的詞法分析器和遞歸下降分析(recursive descent parsing),他們采用的是NFA到DFA的詞法生成器,以及一個(gè)LR分析器,然后再掃描一遍將解析樹(shù)轉(zhuǎn)換成AST(抽象語(yǔ)法樹(shù),是更方便的代碼表示形式)。這需要占用更多代碼,占了2677行,比我們的1705行大約多了1k行。

    ?

  • 他們使用的是更漂亮的通用AST類(lèi)型,能轉(zhuǎn)換成不同的類(lèi)型參數(shù),因?yàn)槊看谓馕龆紩?huì)添加更多信息。這需要更多的輔助函數(shù),因此導(dǎo)致了他們的AST代碼比我們的實(shí)現(xiàn)多了500行——我們?cè)诮馕霾⑻砑有畔r(shí)使用的只是結(jié)構(gòu)字面量,和可修改的Option<_>字段。

    ?

  • 他們大約有400多行代碼用于實(shí)現(xiàn)更高的抽象程度,從而用純粹的函數(shù)式方式來(lái)實(shí)現(xiàn)代碼生成和組合,而我們是直接修改字符串。

?

這些差異再加上測(cè)試用例的差異,就導(dǎo)致了代碼行數(shù)的差別。實(shí)際上,我們的文件在中間解析階段(如常量折疊、作用域解析等)的大小跟他們的非常接近。但依然產(chǎn)生了字節(jié)數(shù)上的區(qū)別,原因是行的平均長(zhǎng)度,我估計(jì)原因是他們需要更多的代碼,在每次解析時(shí)重寫(xiě)整個(gè)樹(shù),而我們只需要訪問(wèn)并修改即可。

?

我認(rèn)為,考慮到Rust和Haskell的設(shè)計(jì)決定非常相似,都是表達(dá)性的,只有細(xì)微的差異,如Rust在需要時(shí)能夠很方便地修改變量等。另一點(diǎn)有意思的是,我們選擇采用遞歸下降分析器和手工編寫(xiě)詞法分析器給我們帶來(lái)了回報(bào)。雖然這有點(diǎn)風(fēng)險(xiǎn),因?yàn)榻淌诓](méi)有推薦這一點(diǎn),我是自學(xué)來(lái)的,但我發(fā)現(xiàn)它很易于使用,是個(gè)正確的決定。

?

我認(rèn)為,這個(gè)團(tuán)隊(duì)可能并沒(méi)有開(kāi)發(fā)出Haskell的全部潛力。如果他們能更善于使用Haskell,他們的代碼應(yīng)該行數(shù)更少。我相信,像Edward Kmeet之類(lèi)的人可以使用更少的Haskell代碼就能編寫(xiě)出同樣的編譯器,從這一點(diǎn)上來(lái)說(shuō),我朋友的團(tuán)隊(duì)并沒(méi)有使用太多超高級(jí)的抽象,而且他們也不允許使用更好的組合庫(kù),如lens等。但是,這樣做的代價(jià)就是理解編譯器的難度。團(tuán)隊(duì)的成員都是有經(jīng)驗(yàn)的程序員,他們知道Haskell可以做非常漂亮的事情,但還是決定不這樣做,因?yàn)樗麄冋J(rèn)為,這樣做花費(fèi)的時(shí)間會(huì)超過(guò)節(jié)省的時(shí)間,而且會(huì)讓代碼變得難以理解。在我看來(lái)這的確是個(gè)正確的選擇,用“魔法”的方式使用Haskell編寫(xiě)編譯器,會(huì)產(chǎn)生“Haskell寫(xiě)編譯器的門(mén)檻非常高,如果你不考慮對(duì)于不太了解Haskell的人的可維護(hù)性的話”的結(jié)果,而這種結(jié)果并不是我們想要的。

?

另一個(gè)有趣的發(fā)現(xiàn)是,教授在開(kāi)始時(shí)說(shuō)過(guò),學(xué)生可以選擇任何能夠在學(xué)校服務(wù)器上運(yùn)行的語(yǔ)言,但同時(shí)針對(duì)Haskell提出了警告,說(shuō)過(guò)去使用Haskell的團(tuán)隊(duì)的分?jǐn)?shù)的方差是最高的,因?yàn)樵S多選擇Haskell的團(tuán)隊(duì)都高估了他們的Haskell能力,導(dǎo)致他們的得分比選擇其他語(yǔ)言的團(tuán)隊(duì)低得多,也有另一部分Haskell團(tuán)隊(duì)像我朋友那樣做得非常完美。

?

?

640?wx_fmt=png

C++

?

?

接下來(lái)我與另一個(gè)在團(tuán)隊(duì)中使用了C++的朋友進(jìn)行了交談。那個(gè)團(tuán)隊(duì)中我只認(rèn)識(shí)這一個(gè)人,但由于滑鐵盧大學(xué)中使用C++的課程非常普遍,所以估計(jì)團(tuán)隊(duì)中的每個(gè)人都有C++經(jīng)驗(yàn)。

?

他們的項(xiàng)目代碼行數(shù)為8733,字節(jié)數(shù)為280kb,這些數(shù)字不包括測(cè)試代碼,但包括大約500行的額外功能。與我們不含測(cè)試的代碼(也包含500行的額外功能)相比,他們的代碼行數(shù)為1.4倍。他們通過(guò)了100%的公開(kāi)測(cè)試,但僅通過(guò)了90%的秘密測(cè)試,很可能是因?yàn)樗鼈儧](méi)有實(shí)現(xiàn)項(xiàng)目要求的數(shù)組vtable,這個(gè)功能需要大約50-100行代碼實(shí)現(xiàn)。

?

我并沒(méi)有深入挖掘代碼差異的原因,我感覺(jué)最有可能的解釋為:

?

  • 他們使用了LR解析器和樹(shù)重寫(xiě),而沒(méi)有采用遞歸下降分析器;

  • C++缺乏匯總類(lèi)型和模式匹配這兩個(gè)非常常用的功能;

  • 他們需要重復(fù)頭文件中所有的函數(shù)簽名,而Rust不需要這樣做。

?

我們比較的另一件事是編譯時(shí)間。在我的筆記本上,我們的編譯器的調(diào)試版完整編譯需要9.7秒,調(diào)試版增量編譯需要3.5秒。我的朋友并沒(méi)有給出他們的C++編譯器的構(gòu)建時(shí)間(采用并行make),但說(shuō)我提供的數(shù)字與他們的非常接近,而且說(shuō)他們把一些常用的小函數(shù)的簽名放到了頭文件中,以增加編譯時(shí)間為代價(jià)來(lái)減少函數(shù)簽名的重復(fù)(也正是由于這個(gè)原因,我沒(méi)有辦法比較單純的頭文件代碼行數(shù))。

?

?

640?wx_fmt=png

Python

?

?

我的一位朋友是非常優(yōu)秀的程序員,她選擇使用Python獨(dú)立完成項(xiàng)目。她還比其他團(tuán)隊(duì)多實(shí)現(xiàn)了好幾個(gè)額外功能,包括帶有寄存器分配的SSA立即表示,還有其他優(yōu)化。另一方面,由于她是獨(dú)立完成的,而且實(shí)現(xiàn)了許多額外的功能,因此她在代碼質(zhì)量上只花費(fèi)了最小限度的經(jīng)歷,例如所有錯(cuò)誤都會(huì)拋出統(tǒng)一的異常(所以調(diào)試時(shí)需要進(jìn)行棧跟蹤),而不是像我們一樣每種錯(cuò)誤都給出特定的錯(cuò)誤類(lèi)型和錯(cuò)誤信息。

?

她的編譯器只有4581行,并且通過(guò)了所有公開(kāi)測(cè)試和秘密測(cè)試。她實(shí)現(xiàn)的功能比所有其他團(tuán)隊(duì)都多得多,但很難確定那些功能占了多少行代碼,因?yàn)樵S多額外功能與每個(gè)人都在做的功能都相同,比如常量折疊、代碼生成等,但功能卻更強(qiáng)大。額外的功能估計(jì)至少占用了1000~2000行,所以我很確信她的代碼的表達(dá)性要比我們至少高兩倍。

?

造成這種差異的最大原因很可能是動(dòng)態(tài)類(lèi)型。我們的ast.rs中類(lèi)型定義就占了500行,編譯器的其他部分還有更多的類(lèi)型定義。我們還通過(guò)類(lèi)型系統(tǒng)做了各種類(lèi)型限制。例如,我們需要基礎(chǔ)設(shè)施,才能在分析代碼過(guò)程中向AST中添加信息供以后使用,而Python中只需要給AST結(jié)點(diǎn)添加新的域即可。

?

強(qiáng)大的元編程也是造成差異的原因之一。例如,盡管她用的是LR分析器而不是遞歸下降分析器,但她的項(xiàng)目代碼量更小,因?yàn)樗恍枰M(jìn)行樹(shù)重寫(xiě)的過(guò)程,而是在LR語(yǔ)法中加入了Python代碼片段來(lái)構(gòu)建AST,而生成器可以直接利用eval變成Python函數(shù)。我們沒(méi)有采用LR分析器的部分原因是,不使用樹(shù)重寫(xiě)來(lái)構(gòu)建AST需要大量的代碼(生成的Rust文件或過(guò)程式的宏)將語(yǔ)法綁定到Rust代碼片段上。

?

元編程和動(dòng)態(tài)類(lèi)型的強(qiáng)大之處的另一個(gè)例子是,我們有個(gè)名為visit.rs的文件有400行,里面大部分是重復(fù)性的樣板代碼,僅為了實(shí)現(xiàn)在各種AST結(jié)構(gòu)上的訪問(wèn)。在Python中只需要一個(gè)大約10行的函數(shù)即可遞歸地訪問(wèn)AST結(jié)點(diǎn)的各個(gè)域(通過(guò)__dict__屬性)。

?

作為Rust和靜態(tài)類(lèi)型語(yǔ)言的愛(ài)好者,我需要指出,類(lèi)型系統(tǒng)非常有助于避免bug和提高性能。強(qiáng)大的元編程同時(shí)會(huì)讓代碼更難理解,但是,這個(gè)比較結(jié)果依然讓我非常驚訝,我沒(méi)想到代碼的差異能有如此之大。如果差異真的導(dǎo)致需要寫(xiě)兩倍的代碼,那我依然認(rèn)為Rust的付出是值得的,但兩倍的差異的確不可忽視,我以后會(huì)考慮在獨(dú)立完成某項(xiàng)工作中的一次性代碼時(shí)使用Ruby或Python。

?

C++、Python、Rust、Scala 構(gòu)建編譯器的差異性究竟有多大?_第2張圖片

?

?

640?wx_fmt=png

Rust(另一個(gè)組)

?

?

最后一個(gè)比較,也是最有意思的,就是我和另一個(gè)朋友的比較。他們組還有另一個(gè)成員(我不認(rèn)識(shí)),使用的也是Rust。我的朋友有許多Rust經(jīng)驗(yàn),也參與過(guò)Rust編譯器,也讀過(guò)許多資料。但我不了解他的組員如何。

?

他們的項(xiàng)目有17,211行代碼,不算注釋的話有15000行,不包括測(cè)試代碼和生成的代碼共有637kb。他們沒(méi)有實(shí)現(xiàn)任何額外功能,僅通過(guò)了4/10個(gè)秘密測(cè)試,以及90%的公開(kāi)測(cè)試,因?yàn)樗麄儧](méi)有時(shí)間在截止日期之前實(shí)現(xiàn)項(xiàng)目要求中的高級(jí)部分。同樣的語(yǔ)言,代碼量卻是我們的三倍,但功能卻更少!

?

這個(gè)結(jié)果非常讓我吃驚,與之相比,之前的比較都黯然無(wú)光了。所以我們比較了wc -l中的每個(gè)文件大小,以及仔細(xì)檢查各個(gè)功能是怎樣實(shí)現(xiàn)的。

?

似乎我們做出的設(shè)計(jì)決定完全不一樣。例如,他們的前端(詞法、解析、AST構(gòu)建)包括7597行,而我們的只有2164行。他們使用的是基于DFA的詞法分析器和LALR(1)語(yǔ)法分析器,但其他采用了類(lèi)似方案的組并沒(méi)有寫(xiě)如此之多的代碼。仔細(xì)檢查他們的代碼后,我發(fā)現(xiàn)了許多不同的設(shè)計(jì)決定:

?

  • 他們采用了有完整類(lèi)型的解析樹(shù),而不是標(biāo)準(zhǔn)的、基于字符串的同態(tài)解析樹(shù)。因此需要更多類(lèi)型定義,以及解析過(guò)程中需要更多的轉(zhuǎn)換代碼,或者需要更復(fù)雜的解析生成器。

?

  • 他們?cè)隍?yàn)證正確性時(shí),使用了TryFrom在解析樹(shù)類(lèi)型和AST類(lèi)型之間互相轉(zhuǎn)換,這導(dǎo)致了大量的10~20行的impl代碼塊。我們使用了返回Result類(lèi)型的函數(shù)來(lái)實(shí)現(xiàn)同樣的功能,額外代碼量更小,也不必對(duì)結(jié)構(gòu)過(guò)度添加類(lèi)型,從而參數(shù)的重用更容易。我們的部分代碼僅有一行match,對(duì)于他們則需要10行的impl語(yǔ)句。

?

  • 我們的類(lèi)型需要更少的復(fù)制粘貼。例如,他們?cè)O(shè)置了單獨(dú)的is_abstract、is_native和is_static域,由此導(dǎo)致的約束使得檢驗(yàn)的代碼需要被復(fù)制粘貼兩次,一次在不返回結(jié)果的方法中,另一次在返回結(jié)果的方法中,兩者只有微小的修改。對(duì)于我們來(lái)說(shuō),void只是一個(gè)特殊的類(lèi)型,我們想出了一個(gè)方法,按照mode和visibility分類(lèi),從而在類(lèi)型的層次上保證這些約束,約束的錯(cuò)誤由match語(yǔ)句的default case生成,可以直接轉(zhuǎn)變成mode和visibility所需的modifier。

?

我沒(méi)有查看他們代碼中的分析過(guò)程,但這個(gè)過(guò)程也一樣大。我跟我的朋友聊了聊,似乎他們的實(shí)現(xiàn)跟我們的訪問(wèn)者基礎(chǔ)架構(gòu)完全不一樣。我猜其他一些小的設(shè)計(jì)差異也導(dǎo)致了代碼量的區(qū)別。

?

訪問(wèn)者模式讓我們的分析過(guò)程只需要關(guān)注它們需要關(guān)注的AST,而不用去匹配整個(gè)AST結(jié)構(gòu),從而節(jié)省了大量代碼。

?

他們的代碼生成部分是3594行,我們的只有1560行。我看了他們的代碼,似乎所有的差異都在于他們采用了一種中間數(shù)據(jù)結(jié)構(gòu)來(lái)生成匯編指令,而我們只使用了基本的字符串直接輸出匯編代碼。他們的做法需要為所有的指令和操作數(shù)定義類(lèi)型和輸出函數(shù),這也意味著,構(gòu)建匯編指令需要耗費(fèi)更多的代碼,而我們的只需要使用類(lèi)似于mov ecx, [edx]的指令,而他們需要一條巨大得被rustfmt分割成6行的語(yǔ)句,其中生成指令時(shí),操作數(shù)使用了許多中間類(lèi)型,還涉及了多達(dá)6層的嵌套括號(hào)。我們的輸出部分也只是一個(gè)格式化語(yǔ)句,而他們需要為每條指令單獨(dú)構(gòu)造。

?

我的團(tuán)隊(duì)也曾考慮過(guò)使用這種級(jí)別的抽象。如果能直接輸出文本形式的匯編,或者直接輸出機(jī)器碼,那就會(huì)方便許多,但這并不是課程的要求。同樣的東西可以使用X86Writer加上類(lèi)似于push(reg: Register)之類(lèi)的方法很簡(jiǎn)單地完成,代碼量更少,效率更高。我們考慮過(guò)的另一個(gè)角度是,抽象也許能讓調(diào)試和測(cè)試更簡(jiǎn)單,但我們意識(shí)到,直接查看生成的文本匯編,可能會(huì)更容易閱讀和測(cè)試。但我們預(yù)測(cè)到(顯然是正確的),那樣做會(huì)導(dǎo)致大量的額外代碼,而且并不能給我們帶來(lái)任何實(shí)際的好處,所以我們沒(méi)有做。

?

可以跟C++那個(gè)組使用的中間表示形式做個(gè)比較。他們將中間表示形式作為額外功能來(lái)實(shí)現(xiàn),占用了大約500行代碼。他們采用的數(shù)據(jù)結(jié)構(gòu)非常簡(jiǎn)單(用于簡(jiǎn)單的類(lèi)型定義和代碼生成),它采用的操作與Java要求的很接近。也就是說(shuō),他們的IR比生成的匯編更小(因此需要的構(gòu)造代碼更少),因?yàn)樵S多語(yǔ)言的操作(如調(diào)用、強(qiáng)制類(lèi)型轉(zhuǎn)換等)需要大量的匯編指令。高層表示也使他們得以在IR上做一些簡(jiǎn)單的優(yōu)化。C++團(tuán)隊(duì)想出了一個(gè)非常好的設(shè)計(jì),所以他們能用更少的代碼完成更多的功能。

?

總的來(lái)看,3倍的代碼量似乎完全由不同的設(shè)計(jì)決定導(dǎo)致,每個(gè)設(shè)計(jì)決定的不同都導(dǎo)致了或大或小的代碼量增加。他們實(shí)現(xiàn)了大量我們沒(méi)有做的抽象,增加了許多代碼,反而我們實(shí)現(xiàn)的一些能減少代碼的抽象他們卻沒(méi)有做。

?

這個(gè)結(jié)果讓我非常驚訝。我知道設(shè)計(jì)決定很重要,但我沒(méi)想到會(huì)導(dǎo)致如此大的差異。考慮到我只調(diào)查了我認(rèn)為很厲害的程序員的情況下,這個(gè)結(jié)果更讓我震驚。在所有的比較中,這個(gè)比較讓我學(xué)到的東西最多。

?

我認(rèn)為有幫助的是,我在選這門(mén)課之前讀了許多關(guān)于怎樣編寫(xiě)編譯器的東西,所以我可以借鑒他人的好的設(shè)計(jì),發(fā)現(xiàn)AST訪問(wèn)者、遞歸下降分析等在課程中沒(méi)有教過(guò)的方法真得很好用。

?

我認(rèn)真考慮的一件事就是抽象的代價(jià)。抽象可以讓代碼在未來(lái)更容易擴(kuò)展,或者能防止特定類(lèi)型的錯(cuò)誤,但需要認(rèn)真考慮,因?yàn)樗赡軙?huì)導(dǎo)致三倍的代碼量,增加理解和重構(gòu)的工作量,也讓可能出現(xiàn)bug的位置增加了三倍,導(dǎo)致測(cè)試和后續(xù)開(kāi)發(fā)的時(shí)間更少。我們的課程跟真實(shí)情況不一樣的是,我們很清楚地知道我們需要實(shí)現(xiàn)什么,而且我們永遠(yuǎn)不需要回過(guò)頭來(lái)維護(hù)代碼,所以完全抵消了抽象帶來(lái)的好處。但是,如果你想讓我擴(kuò)展編譯器,添加任意新功能,而我可以選擇從哪個(gè)編譯器上開(kāi)始工作,那我肯定會(huì)選擇我們自己的代碼(即使不是出于熟悉的原因)。因?yàn)槲覀兊拇a不僅代碼量更少,更容易理解,而且我還可以在知道需要擴(kuò)展后想出一個(gè)更好的抽象方法(就像C++團(tuán)隊(duì)的IR那樣)。

?

我還鞏固了分類(lèi)法的抽象,盡管我的目的只是根據(jù)當(dāng)前的需求(如訪問(wèn)者模式)來(lái)刪除代碼,以及根據(jù)當(dāng)前的需求添加抽象而已,但它還能提供可擴(kuò)展性、可調(diào)試性和正確性等。

?

?

640?wx_fmt=png

Scala

?

?

我還跟一個(gè)上學(xué)期用Scala的朋友討論過(guò),他們的項(xiàng)目跟我們的完全一樣。他們的編譯器包含4141行,160kb(不算測(cè)試)。他們通過(guò)了8/10個(gè)秘密測(cè)試和100%的公共測(cè)試,沒(méi)有實(shí)現(xiàn)任何額外功能。所以與我們的5906行代碼相比,他們的代碼只有0.7倍。

?

他們的代碼更少的原因之一就是他們采用了不同的語(yǔ)法分析方式。這門(mén)課程允許你使用LR表生成器工具,這個(gè)團(tuán)隊(duì)就使用了,而我之前提到的任何團(tuán)隊(duì)都沒(méi)有使用。使用這個(gè)工具后,他們就不需要自己實(shí)現(xiàn)LR表生成器。他們還從Java語(yǔ)法網(wǎng)站上找到了一段150行的Python腳本,該腳本從Java語(yǔ)法網(wǎng)站的頁(yè)面上搜集語(yǔ)法并轉(zhuǎn)換成了生成工具的輸入,從而他們不必自己寫(xiě)LR語(yǔ)法。他們依然要用Scala構(gòu)建樹(shù),但他們整個(gè)分析階段只用了1073行,而我們用了1443行,大部分采用LR分析的其他團(tuán)隊(duì)的代碼量都比我們的遞歸下降分析更多。

?

他們的編譯器的其余部分比我們的更小,但沒(méi)有明顯的設(shè)計(jì)區(qū)別,盡管我沒(méi)有深入閱讀代碼。我認(rèn)為原因應(yīng)該是Scala和Rust語(yǔ)言之間的表示區(qū)別。Scala和Rust擁有類(lèi)似的函數(shù)式編程功能,如模式匹配,這對(duì)于編譯器很有用,但Scala的受管理的內(nèi)存能節(jié)省下一些代碼。Scala還比Rust有更多的語(yǔ)法糖。

?

?

640?wx_fmt=png

OCaml

?

?

由于我們團(tuán)隊(duì)所有人都在Jane Street實(shí)習(xí),所以我們考慮過(guò)的另一門(mén)語(yǔ)言是OCaml。我們最后決定用Rust,但很想知道OCaml會(huì)怎樣。所以我與另一個(gè)也在Jane Street實(shí)習(xí)的人談了談,他們的編譯器就是用OCaml做的。

?

他們的編譯器是10914行,377kb,包括一小部分測(cè)試代碼,沒(méi)有額外功能,通過(guò)了9/10的秘密測(cè)試和所有的公開(kāi)測(cè)試。

?

與其他組類(lèi)似,代碼量的差異是由于他們采用了LR分析器生成器和樹(shù)重寫(xiě),詞法分析采用了正則表達(dá)式->NFA->DFA轉(zhuǎn)換管線。他們的前端(詞法分析+語(yǔ)法分析+AST構(gòu)建)包含5548行,我們的只有2164行,字節(jié)比例類(lèi)似。他們對(duì)于語(yǔ)法分析器也用了expect tests,我們也使用了類(lèi)似的測(cè)試,但將預(yù)期的輸出放到了代碼之外,所以他們的分析器測(cè)試占了大約600行,而我們的只有200行。

?

他們的編譯器的其余部分是5366行(其中461行是僅有類(lèi)型定義的接口文件),而我們的是4642行,如果考慮接口定義則只有1.15倍差異,不考慮接口定義,兩者則幾乎是同樣大小。所以,除了語(yǔ)法分析器的設(shè)計(jì)不一樣之外,Rust和OCaml的表達(dá)性很相似,除了OCaml需要一些Rust不需要的接口定義而已。

別驚訝!人工智能時(shí)代即將到來(lái)!

https://edu.csdn.net/topic/ai30?utm_source=csdn_bw

?

640?wx_fmt=png

總結(jié)

?

?

總的來(lái)說(shuō),我對(duì)于比較結(jié)果非常滿意。

?

我從此次比較中學(xué)到了許多,也發(fā)現(xiàn)了許多令我驚訝的地方。我認(rèn)為整體來(lái)說(shuō),設(shè)計(jì)決定造成的影響要遠(yuǎn)遠(yuǎn)大于語(yǔ)言的選擇,而在實(shí)現(xiàn)不同的設(shè)計(jì)時(shí),語(yǔ)言也是重要的,因?yàn)檎Z(yǔ)言提供了實(shí)現(xiàn)設(shè)計(jì)的工具。

?

原文:http://thume.ca/2019/04/29/comparing-compilers-in-rust-haskell-c-and-python/

本文為 CSDN 翻譯,轉(zhuǎn)載請(qǐng)注明來(lái)源出處。

?

C++、Python、Rust、Scala 構(gòu)建編譯器的差異性究竟有多大?_第3張圖片

?熱 文 ?推 薦?

? 微信解封快手鏈接;AWS 證實(shí)宕機(jī);微軟內(nèi)部疑禁用 Slack | 極客頭條

? 為什么我不使用 Web 組件?

V 語(yǔ)言強(qiáng)勢(shì)登頂 GitHub TOP1,欲取 Go 而代之?

? 華為最強(qiáng)自研 NPU 問(wèn)世,麒麟 810 “拋棄”寒武紀(jì)

? LinkedIn最新報(bào)告: 區(qū)塊鏈成職位需求增長(zhǎng)最快領(lǐng)域, 這些地區(qū)對(duì)區(qū)塊鏈人才渴求度最高……

? 中文NLP的分詞真有必要嗎?李紀(jì)為團(tuán)隊(duì)四項(xiàng)任務(wù)評(píng)測(cè)一探究竟 | ACL 2019

? 6月技術(shù)福利限時(shí)免費(fèi)領(lǐng)

? 搞不懂SDN?那是因?yàn)槟銢](méi)看這個(gè)小故事…

她說(shuō):程序員離開(kāi)電腦就是 “廢物” !

C++、Python、Rust、Scala 構(gòu)建編譯器的差異性究竟有多大?_第4張圖片 點(diǎn)擊閱讀原文,輸入關(guān)鍵詞,搜索CSDN文章。

640?wx_fmt=png 你點(diǎn)的每個(gè)“在看”,我都認(rèn)真當(dāng)成了喜歡


更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主

微信掃碼或搜索:z360901061

微信掃一掃加我為好友

QQ號(hào)聯(lián)系: 360901061

您的支持是博主寫(xiě)作最大的動(dòng)力,如果您喜歡我的文章,感覺(jué)我的文章對(duì)您有幫助,請(qǐng)用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點(diǎn)擊下面給點(diǎn)支持吧,站長(zhǎng)非常感激您!手機(jī)微信長(zhǎng)按不能支付解決辦法:請(qǐng)將微信支付二維碼保存到相冊(cè),切換到微信,然后點(diǎn)擊微信右上角掃一掃功能,選擇支付二維碼完成支付。

【本文對(duì)您有幫助就好】

您的支持是博主寫(xiě)作最大的動(dòng)力,如果您喜歡我的文章,感覺(jué)我的文章對(duì)您有幫助,請(qǐng)用微信掃描上面二維碼支持博主2元、5元、10元、自定義金額等您想捐的金額吧,站長(zhǎng)會(huì)非常 感謝您的哦!!!

發(fā)表我的評(píng)論
最新評(píng)論 總共0條評(píng)論