.NET 程式碼編譯過程

ForTechnology發表於2012-10-11
作為一種程式碼指令平臺,Microsoft .NET比微軟公司先前推出的其他技術平臺要來得更為複雜。由於.NET提供了對多種程式語言以及(在理論上說)多重平臺的支援,這就需要在傳統的兩個代 碼層新增一箇中間程式碼層。在這裡,傳統的兩層分別是原始碼層和編譯後的本機程式碼層。新加的程式碼層給.NET平臺帶來了額外的靈活性,不過,反過來卻又增加 了系統的複雜性。此外,由於這一新程式碼層的出現,一連串的新型應用程式部署選項也首次展現在了程式設計師的面前。

.NET之與眾不同:MSIL

        在Microsoft .NET框架內,應用程式可以用好多種高階程式語言編寫、建立,例如VB.NET、C#乃至COBOL .NET等等都可以編寫.NET應用程式。而通過每一種遵守.NET規範的程式語言所編寫的程式程式碼首先都得通過一種初始編譯步驟從原始碼變成.NET的 公共標準語言:MSIL(微軟中介語言:Microsoft Intermediate Language)。MSIL自身是一種完整的、和物件相關的語言,只有它才可能建立出應用程式。為了大致瞭解MSIL的一些有關情況,你可以參看“通過 MSIL語言瞭解CLR的執行原理”一文。.NET應用程式是以MSIL的形式出現的,只有在程式執行的時候才通過即時編譯器(JIT)被編譯為本機代 碼。

.NET的編譯過程:從原始碼到本機指令

        只要裝載了assembly程式碼就會進行JIT編譯,可見這是一種彙編級的編譯(瞭解更多assembly技術的細節請參看“Assembly—治癒 “DLL地獄”的良方?”一文)。在編譯過程中,JIT編譯器一旦首次遭遇物件的索引就會裝載匹配物件各個方法宣告的對應程式。這樣,以後呼叫方法的時候 就會編譯其IL,而方法的對應根程式則被方法的編譯後程式碼的地址所取代。這一過程在每次方法被首次呼叫的時候進行,產生的本機程式碼則被緩衝以便會話過程中 下次裝載assembly程式碼的時候可以被使用。顯然,這樣的指令系統相比傳統的編譯語言需要更大的處理能力,不過其要求也沒有你想象的那麼高。

    在這裡必須澄清一個普遍誤解的錯誤概念,那就是不少人認為.NET應用程式是解釋型而非編譯型的程式。另外,還有這樣的常見錯誤認識:JIT編譯的程式碼存 儲在磁碟上並且可以為同一應用程式執行。雖然這樣做也不是不可以,但是,你很快就會明白,這可不是預設的編譯方案。應用程式的IL程式碼實際上在每次應用程 序執行的時候都會被重新編譯為本機程式碼。

兩種編譯器

    事實上,JIT編譯器分成兩種(經濟編譯器和普通編譯器),而且它們生來也不是平等的。經濟JIT編譯器代表了執行一個.NET應用程式所需要的最少功 能,它直接用對等的本機程式碼取代每一條MSIL指令,不進行任何優化從而也帶來更少的系統負載。這也意味著它主要應用在記憶體等資源比較緊張的平臺上。

    另一方面,普通JIT編譯器則是預設的執行時配置,它會對其產生的程式碼進行即時優化。這樣做無形中給予了.NET超出傳統預編譯語言的一個優點:預編譯語 言只能對其處理的程式碼將要執行於其上的平臺做一番大致的事前估計。JIT編譯器可以經過準確調節達到當前執行時狀態,結果可以完成一些預編譯語言無法完成 的工作:

·更高效地利用和分配CPU暫存器
·在適當的情況下實施低階程式碼優化,比如常量重疊、拷貝複製、取消範圍檢查、取消常規副表示式以及方法內聯等
·在程式碼執行期間監控當前的物理和虛擬記憶體需求從而更高效地利用記憶體
·產生特定的平臺指令以準確、充分地利用實際的處理器模式

        .NET編譯的結果就是JIT所帶來的額外負載要求並沒有產生顯著的效能損失。

效能選項

        這就是說,每次執行應用程式時MSIL就會被JIT編譯。記住,這就是常識了,然後,根據以上內容中說明的原理,在開始啟動應用程式以及首次使用非核心功能的時候顯然會導致低於優化級的系統效能表現。那麼你又該採取什麼措施把這種負面影響降低到最小呢?

        微軟公司的對策是為我們提供了一種名為Pre-JIT的編譯器(也被稱做本機映像生成器:Native Image Generator,程式名因此是Ngen.exe)。從表面上看,至少它也算是應付任何效能問題的一項治療手段。Pre-JIT編譯器在執行時之前被調 用,在安裝時,它會把全部assembly形式的MSIL編譯為本機程式碼。這種本機程式碼隨後儲存在全域性assembly快取(Global Assembly Cache)的某一個特殊部分供以後使用,這樣就完全繞過了JIT編譯過程。

        乍看之下,這樣做應該是解決先前的問題了,對客戶端程式碼而言尤其如此。但是,你還記得嗎?普通JIT在編譯MSIL的時候實施了大量的即時優化操作。而許 多此類的優化操作,尤其是那些牽扯到暫存器和儲存器使用的優化,都是由系統的當前需求所驅動的。所以,批量編譯assembly程式碼的舉措就會阻止這些優 化的進行從而在實際上產生出執行更慢的最終程式碼。在你採用這個法子之前,微軟的建議是,比照普通編譯下的當前條件,把你的JIT和Ngen版本設定為目標 平臺上的同一彙編級。

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/25897606/viewspace-746232/,如需轉載,請註明出處,否則將追究法律責任。

相關文章