.NET編譯技術內幕(2)

來源:互聯網
上載者:User
編譯 .NET編譯技術內幕(2)
作者: builder.com
Tuesday, April 16 2002 12:23 PM

作為一種代碼指令平台,Microsoft .NET比微軟公司先前推出的其他技術平台要來得更為複雜。由於.NET提供了對多種程式設計語言以及(在理論上說)多重平台的支援,這就需要在傳統的兩個代碼層添加一個中間代碼層。在這裡,傳統的兩層分別是原始碼層和編譯後的機器碼層。新加的代碼層給.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)被編譯為機器碼。圖A就對這一過程進行了說明。

圖A



.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版本設定為目標平台上的同一彙編級。

除非你是Java的鐵杆擁躉,.NET的運行時行為和編譯器都將同你以前曾經瞭解的同類情況完全不同。但是它們也不是什麼神秘的東西。我希望我的闡述能澄清一些曾經令你感到迷惑的問題。

相關文章

E-Commerce Solutions

Leverage the same tools powering the Alibaba Ecosystem

Learn more >

Apsara Conference 2019

The Rise of Data Intelligence, September 25th - 27th, Hangzhou, China

Learn more >

Alibaba Cloud Free Trial

Learn and experience the power of Alibaba Cloud with a free trial worth $300-1200 USD

Learn more >

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。