Native代碼產生器: NGen.exe

來源:互聯網
上載者:User

      隨.NET Framework發布的NGen.exe工具可以將IL代碼編譯成native代碼, 當應用程式安裝在使用者的機器上時. 因為代碼是在安裝的時候編譯的, CLR的JIT編譯器不需要在運行時刻編譯IL代碼, 這能提高應用程式的效能. NGen.exe工具在下面兩個場合很有趣:

  •    提高了應用程式的啟動速度: 運行NGen.exe能提高啟動速度, 因為代碼已經編譯成native代碼, 所以在運行時就不需要編譯了.
  •    減少應用程式的工作集: 如果你認為一個程式集會被同時載入到多個進程/Appdomain, 在這個程式集上運行NGen.exe能減少應用程式的工作集, 其原因是NGen.exe工具將IL編譯成native代碼, 然後將輸出儲存到單獨的檔案中, 這個檔案能同時被記憶體映射(memory-mapping)到多個進程地址空間中, 允許代碼共用, 每個進程/AppDomain不必為自己拷貝一份代碼.

      當一個安裝程式調用NGen.exe對一個應用程式或程式集進行編譯時間, 那個應用程式的所有程式集或者一個特定的程式集會把其IL代碼編譯成native代碼, 一個新的只包含native代碼而不含有IL的組件檔會被NGen.exe建立. 這個新的檔案被放到名字類似於 C:\Windows\Assembly\NativeImages_v2.0.50727_32的檔案夾下面, 這個檔案家名字包含了CLR的版本和native代碼是否是為x86(32位版本的Windows), x64, 或者Itaninum(64位版本的Windows)編譯的資訊.

      現在, 當CLR載入一個組件檔時, CLR查看對應的NGen’d native檔案是否存在, 如果沒發現native檔案, CLR JIT對IL代碼像通常那樣進行編譯. 然而, 如果對應的native檔案存在, CLR將使用native檔案中的編譯好的代碼, 檔案中的函數就不需要在運行時刻編譯了.

      在表面上, 這聽起來非常好, 聽上去就像如果你得到了Managed 程式碼的全部優點(記憶體回收, 代碼驗證, 型別安全, 等等)而不犧牲Managed 程式碼的效能(JIT編譯), 但是實際情況並不總是那麼美好, NGen’d檔案有幾個潛在的問題:

   沒有智慧財產權保護: 很多人以為發行就緒NGen檔案而不用發布包含原始IL代碼的檔案, 從而使他們的智慧財產權更加保密. 不幸的是, 這並不可行, 在運行時刻, CLR需要訪問程式集的metadata(為某些函數, 例如反射和序列化函數), 這需要發布包含IL和metadata的程式集. 此外, 如果由於某種原因, CLR不能使用NGen’d檔案(如下面所描述的), 那麼CLR會回到JIT編譯, 對程式集的IL代碼進行編譯, 因此IL代碼必須存在.

   NGen檔案可能會過時: 當CLR載入NGen’d檔案時, 它會比較以前編譯的代碼和當前的執行環境的很多特徵, 如果任何特徵不匹配, NGen’d檔案就不能被使用, JIT編譯器進程就要使用. 這是必須被匹配的部分特徵列表.

n  程式集模組的版本ID (MVID)

n  被引用的程式集的版本ID

n  處理器類型

n  CLR版本

n  Build類型(release, debug, optimized debug, profiling, 等等)

      所有連結時的安全性要求都必須在運行時刻被滿足才能允許載入.

      注意有可能以升級的方式運行NGen.exe, 這告訴工具對以前曾經被執行NGen’d的所有的程式集上運行NGen.exe. 當終端使用者安裝.NET Framework的一個新service pack, 那麼service pack的安裝程式將會在更新模式下自動運行NGen.exe, 使得NGen檔案保持和CLR的版本一致.

   較差的載入時效能(重定位/綁定): 組件檔是標準的Windows PE檔案, 每個檔案包含著一個優先使用的基地址. 很多Windows開發人員對圍繞基地址和重定位的問題很熟悉。當JIT編譯代碼時, 不必關心這些問題, 因為正確的記憶體位址引用會在運行時計算出來.

     然而, NGen’d的組件檔的一些記憶體位址引用是靜態計算的, 當Windows載入一個NGen’d檔案時, 它檢查檔案是否被載入到優先的基地址上, 如果檔案沒有載入到優先的基地址, Windows會重新置放檔案, 修改所有記憶體位址引用. 這是極其耗時的, 因為Windows必須載入整個檔案, 並修改檔案中的很多位元組. 此外, 這個分頁檔對應的代碼不能跨進程邊界共用.

     因此如果你打算NGen組件檔, 你應該為你的組件檔選擇好的基地址(通過csc.exe的/baseaddress命令列開關). 當你NGen一個組件檔時, NGen’d檔案將被賦予一個基地址, 這需要使用一個基於託管程式集基地址的演算法. 不幸的是, 微軟從沒有一個良好的指導來協助開發人員如何賦予基地址. 在64位版本的Windows上, 這還不太會成為問題, 因為地址空間是很充足的, 但是對於一個32位的地址空間, 為每一個程式集選擇一個好的基地址幾乎是不可能的, 除非你精確地知道什麼東西會被載入到進程, 知道那個程式集的大小不會超過後一個版本.

   較差的執行時效能: 當編譯代碼時, NGen對執行環境做出的假設不會比JIT編譯器的多, 這會造成NGen.exe產生較差的代碼, 例如, NGen不能最佳化一些CPU指令, 對靜態欄位的訪問需要簡介的操作, 因為靜態欄位實際的地址需要在運行時刻才能知道. NGen到處插入代碼來調用類的建構函式, 因為它不知道代碼執行的次序, 不知道類的構造憾事是否已經被調用了(見第8章, 類的建構函式). 一些NGen’d應用程式會比JIT編譯的代碼慢大約5%, 因此, 如果你打算使用NGen’d來提高應用程式的效能, 你應該對比NGen’d和非NGen’d版本的應用程式, 確定NGen’d版本在實際執行時並不慢. 對於一些應用程式, 減小的工作集大小會提高效能, 因此NGen總體上還是會取勝.

      因為上面列出的所有問題, 當考慮使用NGen.exe時, 你應該非常小心. 對於伺服器端的應用程式來說, NGen.exe的用處很小甚至沒有意義, 因為只有第一個客戶需求經曆了效能上的下降, 後面的客戶需求都是高速啟動並執行. 此外, 對於大多數伺服器應用程式, 只需要代碼的一個執行個體, 因此沒有工作集方面的利益.

      對於用戶端應用程式, NGen.exe可能對於提高啟動速度或者減小工作集有協助, 如果程式集被多個應用程式同時使用. 甚至沒有多個應用程式使用一個程式集, NGen一個程式集也會提高工作集. 此外, 如果NGen.exe被用於所有的用戶端應用程式的程式集, 那麼CLR就根本不需要載入JIT編譯器, 從而更進一步地降低了工作集.    

      當然, 如果只有一個程式集不是NGen’d或者如果一個程式集的NGen’d檔案不能被使用, JIT編譯器就會被載入, 應用程式的工作集將會增加.

聯繫我們

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

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

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.