|
.NET編譯技術(shù)內(nèi)幕(2) 作者: builder.com Tuesday, April 16 2002 12:23 PM
作為一種代碼指令平臺(tái),Microsoft .NET比微軟公司先前推出的其他技術(shù)平臺(tái)要來(lái)得更為復(fù)雜。由于.NET提供了對(duì)多種編程語(yǔ)言以及(在理論上說(shuō))多重平臺(tái)的支持,這就需要在傳統(tǒng)的兩個(gè)代碼層添加一個(gè)中間代碼層。在這里,傳統(tǒng)的兩層分別是源代碼層和編譯后的本機(jī)代碼層。新加的代碼層給.NET平臺(tái)帶來(lái)了額外的靈活性,不過(guò),反過(guò)來(lái)卻又增加了系統(tǒng)的復(fù)雜性。此外,由于這一新代碼層的出現(xiàn),一連串的新型應(yīng)用程序部署選項(xiàng)也首次展現(xiàn)在了程序員的面前。這篇文章的主旨就是引領(lǐng).NET應(yīng)用程序的開(kāi)發(fā)人員理解.NET的代碼編譯過(guò)程,了解附加的編譯選項(xiàng)。
.NET之與眾不同:MSIL 在Microsoft .NET框架內(nèi),應(yīng)用程序可以用好多種高級(jí)程序語(yǔ)言編寫(xiě)、創(chuàng)建,例如VB.NET、C#乃至COBOL .NET等等都可以編寫(xiě).NET應(yīng)用程序。而通過(guò)每一種遵守.NET規(guī)范的編程語(yǔ)言所編寫(xiě)的程序代碼首先都得通過(guò)一種初始編譯步驟從源代碼變成.NET的公共標(biāo)準(zhǔn)語(yǔ)言:MSIL(微軟中介語(yǔ)言:Microsoft Intermediate Language)。MSIL自身是一種完整的、和對(duì)象相關(guān)的語(yǔ)言,只有它才可能創(chuàng)建出應(yīng)用程序。為了大致了解MSIL的一些有關(guān)情況,你可以參看“通過(guò)MSIL語(yǔ)言了解CLR的運(yùn)行原理”一文。.NET應(yīng)用程序是以MSIL的形式出現(xiàn)的,只有在程序執(zhí)行的時(shí)候才通過(guò)即時(shí)編譯器(JIT)被編譯為本機(jī)代碼。圖A就對(duì)這一過(guò)程進(jìn)行了說(shuō)明。
圖A
.NET的編譯過(guò)程:從源代碼到本機(jī)指令
只要裝載了assembly代碼就會(huì)進(jìn)行JIT編譯,可見(jiàn)這是一種匯編級(jí)的編譯(了解更多assembly技術(shù)的細(xì)節(jié)請(qǐng)參看“Assembly—治愈“DLL地獄”的良方?”一文)。在編譯過(guò)程中,JIT編譯器一旦首次遭遇對(duì)象的索引就會(huì)裝載匹配對(duì)象各個(gè)方法聲明的對(duì)應(yīng)程序。這樣,以后調(diào)用方法的時(shí)候就會(huì)編譯其IL,而方法的對(duì)應(yīng)根程序則被方法的編譯后代碼的地址所取代。這一過(guò)程在每次方法被首次調(diào)用的時(shí)候進(jìn)行,產(chǎn)生的本機(jī)代碼則被緩沖以便會(huì)話過(guò)程中下次裝載assembly代碼的時(shí)候可以被使用。顯然,這樣的指令系統(tǒng)相比傳統(tǒng)的編譯語(yǔ)言需要更大的處理能力,不過(guò)其要求也沒(méi)有你想象的那么高。
在這里必須澄清一個(gè)普遍誤解的錯(cuò)誤概念,那就是不少人認(rèn)為.NET應(yīng)用程序是解釋型而非編譯型的程序。另外,還有這樣的常見(jiàn)錯(cuò)誤認(rèn)識(shí):JIT編譯的代碼存儲(chǔ)在磁盤(pán)上并且可以為同一應(yīng)用程序執(zhí)行。雖然這樣做也不是不可以,但是,你很快就會(huì)明白,這可不是缺省的編譯方案。應(yīng)用程序的IL代碼實(shí)際上在每次應(yīng)用程序運(yùn)行的時(shí)候都會(huì)被重新編譯為本機(jī)代碼。
兩種編譯器 事實(shí)上,JIT編譯器分成兩種(經(jīng)濟(jì)編譯器和普通編譯器),而且它們生來(lái)也不是平等的。經(jīng)濟(jì)JIT編譯器代表了運(yùn)行一個(gè).NET應(yīng)用程序所需要的最少功能,它直接用對(duì)等的本機(jī)代碼取代每一條MSIL指令,不進(jìn)行任何優(yōu)化從而也帶來(lái)更少的系統(tǒng)負(fù)載。這也意味著它主要應(yīng)用在內(nèi)存等資源比較緊張的平臺(tái)上。
另一方面,普通JIT編譯器則是缺省的運(yùn)行時(shí)配置,它會(huì)對(duì)其產(chǎn)生的代碼進(jìn)行即時(shí)優(yōu)化。這樣做無(wú)形中給予了.NET超出傳統(tǒng)預(yù)編譯語(yǔ)言的一個(gè)優(yōu)點(diǎn):預(yù)編譯語(yǔ)言只能對(duì)其處理的代碼將要運(yùn)行于其上的平臺(tái)做一番大致的事前估計(jì)。JIT編譯器可以經(jīng)過(guò)準(zhǔn)確調(diào)節(jié)達(dá)到當(dāng)前運(yùn)行時(shí)狀態(tài),結(jié)果可以完成一些預(yù)編譯語(yǔ)言無(wú)法完成的工作:
更高效地利用和分配CPU寄存器 在適當(dāng)?shù)那闆r下實(shí)施低級(jí)代碼優(yōu)化,比如常量重疊、拷貝復(fù)制、取消范圍檢查、取消常規(guī)副表達(dá)式以及方法內(nèi)聯(lián)等 在代碼執(zhí)行期間監(jiān)控當(dāng)前的物理和虛擬內(nèi)存需求從而更高效地利用內(nèi)存 產(chǎn)生特定的平臺(tái)指令以準(zhǔn)確、充分地利用實(shí)際的處理器模式
.NET編譯的結(jié)果就是JIT所帶來(lái)的額外負(fù)載要求并沒(méi)有產(chǎn)生顯著的性能損失。
性能選項(xiàng) 這就是說(shuō),每次運(yùn)行應(yīng)用程序時(shí)MSIL就會(huì)被JIT編譯。記住,這就是常識(shí)了,然后,根據(jù)以上內(nèi)容中說(shuō)明的原理,在開(kāi)始啟動(dòng)應(yīng)用程序以及首次使用非核心功能的時(shí)候顯然會(huì)導(dǎo)致低于優(yōu)化級(jí)的系統(tǒng)性能表現(xiàn)。那么你又該采取什么措施把這種負(fù)面影響降低到最小呢?
微軟公司的對(duì)策是為我們提供了一種名為Pre-JIT的編譯器(也被稱做本機(jī)映像生成器:Native Image Generator,程序名因此是Ngen.exe)。從表面上看,至少它也算是應(yīng)付任何性能問(wèn)題的一項(xiàng)治療手段。Pre-JIT編譯器在運(yùn)行時(shí)之前被調(diào)用,在安裝時(shí),它會(huì)把全部assembly形式的MSIL編譯為本機(jī)代碼。這種本機(jī)代碼隨后存儲(chǔ)在全局assembly緩存(Global Assembly Cache)的某一個(gè)特殊部分供以后使用,這樣就完全繞過(guò)了JIT編譯過(guò)程。
乍看之下,這樣做應(yīng)該是解決先前的問(wèn)題了,對(duì)客戶端代碼而言尤其如此。但是,你還記得嗎?普通JIT在編譯MSIL的時(shí)候?qū)嵤┝舜罅康募磿r(shí)優(yōu)化操作。而許多此類的優(yōu)化操作,尤其是那些牽扯到寄存器和存儲(chǔ)器使用的優(yōu)化,都是由系統(tǒng)的當(dāng)前需求所驅(qū)動(dòng)的。所以,批量編譯assembly代碼的舉措就會(huì)阻止這些優(yōu)化的進(jìn)行從而在實(shí)際上產(chǎn)生出運(yùn)行更慢的最終代碼。在你采用這個(gè)法子之前,微軟的建議是,比照普通編譯下的當(dāng)前條件,把你的JIT和Ngen版本設(shè)置為目標(biāo)平臺(tái)上的同一匯編級(jí)。
除非你是Java的鐵桿擁躉,.NET的運(yùn)行時(shí)行為和編譯器都將同你以前曾經(jīng)了解的同類情況完全不同。但是它們也不是什么神秘的東西。我希望我的闡述能澄清一些曾經(jīng)
|
溫馨提示:喜歡本站的話,請(qǐng)收藏一下本站!