如何看待 .NET Native,真能達到 C++ 的性能、C# 的生產效率嗎?
Microsoft .NET Native FAQ
Getting Started with .NET Native.NET Native 會給 Windows Store App、Windows Phone App 多大幫助?.NET Native 會不會跨平台支持 Linux、OS X 等系統?相關問題:如何評價微軟推出的 .NET Native? (知乎的搜索……)
我對.NET Native抱著很大的信心,相信它能讓許多C#應用接近用C++寫的Windows Store Application的性能。「接近」可能是90%、95%性能,但徹底「達到」100%的C/C++性能還是不太現實。
我覺得這個問題可以參考一下我回答的一個關於Java的靜態優化的問題:逃逸分析為何不能在編譯期進行? - RednaxelaFX 的回答
.NET其實也有類似的問題,不過嚴重程度沒有Java那麼糟糕。Java的動態載入是在Class文件級別,所以安全的做靜態分析和優化的邊界也就在Class文件內;.NET的動態載入是在Assembly級別,而一個Assembly已經包含了許多類型,雖然靜態分析和優化的邊界在Assembly內但已經可以做很多實用的優化。而進一步拓展優化的邊界就需要「放棄」一部分動態載入的靈活性,換來更大的可靜態分析的範圍以便進行更徹底的優化。
在.NET Native之前,.NET已經有一個AOT編譯器——NGen,用於在目標機器上把MSIL編譯到native code並緩存在GAC里。那.NET Native是不是只是NGen舊瓶裝新酒、換湯不換藥呢?
從概念上說,完全不是。
NGen的目的主要是通過事先編譯(AOT)避免(減少)在運行時JIT編譯,提升啟動速度和後續運行的性能穩定性,但不是提升程序的平均或最高性能。這使得它的設計和實現有所取捨:- NGen工作在Assembly單位上,每個MSIL assembly編譯出一個對應的native code assembly。這樣就會遇到前面提到的「優化邊界」問題——跨越Assembly的方法調用全部無法優化。
- NGen其實是把CLR里的JIT編譯器拿來當作AOT編譯器用,在客戶機器上做安裝時編譯,並沒有充分利用靜態編譯做優化可以付出的時間和空間代價,因而最終生成的代碼的優化程度最高也不會比CLR運行時用JIT編譯出來的好。這是個工程取捨。(不過比較新的NGen有profile模式而且可以依據profile信息把熱的代碼塊跟冷的代碼塊分別集中起來,這比JIT的效果好)
- NGen大概是出於靈活性(動態載入的靈活性和打補丁的靈活性)的考慮,生成的代碼是PIC(position-independent code),比起能直接把目標地址嵌入代碼中的JIT來說會損失一點性能。
- .NET Native工作在一個完整的應用的層面,其中所有的Assembly,包括.NET標準庫的Assembly,都會引入進來,一股腦塞進.NET Native的分析、優化、鏈接程序,最終生成一個打包好的可執行程序(附帶程序所需的元數據)。這就突破了Assembly的優化邊界,可以真正在靜態編譯時做全程序分析與優化(whole program analysis),最後得到比較優化的代碼。這跟我前面提到的Java程序的靜態分析那個回答的後半部分說的一致(Excelsior JET的模型)。
- .NET Native不是在客戶機器上編譯,而是在Windows Store的伺服器上編譯,因而無需擔心消耗客戶機器的資源,可以放心大膽開足馬力做編譯優化。
- 我還沒仔細確認,但我猜.NET Native生成出來的代碼不是PIC。
- .NET Native用的編譯器借用了Visual C++的後端(UTC)。雖然NGen借用的CLR的JIT在x64上的版本其實也是借用的UTC,但它們倆具體到底有多少異同我就不知道了。我的猜想(和希望)是.NET Native的使用方式能夠容忍更長的編譯時間,這樣才能有效進行某些複雜度高的優化。
- NGen編譯得到的Assembly仍然需要系統上的CLR來運行,所以目標機器上事先必須安裝.NET Framework;.NET Native則是把一個由CLR極度精簡後的runtime——MRT_app.dll——打包到應用里,不需要目標機器上事先安裝了.NET Framework,因而簡化了部署。
總之.NET Native概念非常好,最終效果好不好就看M$同行們有多努力了哈哈哈 ^_^
===================================
@Thomson 大大在回復里問NGen生成的代碼是PIC的引用資料,有這些:CLR Inside Out: The Performance Benefits of NGen.Improvements to NGen in .NET Framework 4Surupa Biswas: CLR 4Throughput of NGen-compiled code is lower than that of JIT-compiled code primarily for one reason: cross-assembly references. In JIT-compiled code, cross-assembly references can be implemented as direct calls or jumps since the exact addresses of these references are known at run time. For statically compiled code, however, cross-assembly references need to go through a jump slot that gets populated with the correct address at run time by executing a method pre-stub. The method pre-stub ensures, among other things, that the native images for assemblies referenced by that method are loaded into memory before the method is executed. The pre-stub only needs to be executed the first time the method is called; it is short-circuited out for subsequent calls. However, every time the method is called, cross-assembly references do need to go through a level of indirection. This is principally what accounted for the 5-10 percent drop in throughput for NGen-compiled code when compared to JIT-compiled code.
&<- 這描述的就是一種PIC概念的實現。在較新版本的NGen中softbound dependencies仍然是這樣實現的,而新的hardbound dependencies則試圖把地址硬編碼進來。
大家都是從應用開發的角度將這個問題,我要從API開發的角度講。
UWP這個平台是用C++寫的。對,不是C#,沒有.NET,是徹頭徹尾的native,不要再搞錯了。包括構建與其上的控制項,也都要求用C++寫。
原因在於,如果用了C#,即便是.NET Native,也會在載入的時候包含一個GC的runtime,造成一定的內存開銷和啟動性能下降。在PC上還好,手機上就有點明顯了。
不過,我們用C++寫API,測試都是C#的。好寫很多。@Belleve 你約我回答 我總覺得我不應該多說,糾結再三 害得我很難過。我就沒啥可謝的了。
利益相關 微軟員工:負責拿著運行時在客戶現場實施的工程師。
免責:毫無內部資料的情況下,我只能發表個人對於有限技術認知的觀點。
.Net Native 的目的兩點
1 性能提升
2 生產效率的保持
說幾個個人對現狀的理解:
1 為什麼要用這麼極端的方式,而且首先用在Windows Store App上?
回答只提到為了速度的,可能片面理解了性能。
在PC/Server 預熱過的.net jit 不慢,有時比Ngen快,並不能保證新技術編譯的產品快出個數量級,但是預熱耗電,預熱結果緩存耗電。
首先應用在WSA主要目標是:省電。
這一性能指標,在服務端杯水車薪,在移動設備上是決定性的。
2 能起到多大作用
既然是要應用在移動設備上、並且第一階段主要目標是省電
那麼開發者在至少當前這個實現上 是基本看不出變化的,也許,啟動速度真的提升了。嗯,至少所有引用的dll是一起載入的
3 會不會帶來開發上的生產力問題
實際除了遊戲外
我們大部分的Device APP開發都是膠水代碼
膠水的部分可以100%的享受本地編譯的好處
那就是基本不會有問題
反倒是遊戲啊、圖形啊 這類的東西
你要拿的演算法實例大多都是c++的 遷移到 c#是生產力降低吧。。。
除非你引用的類庫,尤其是來自nuget的某個類庫某個付費控制項庫這不支持這j屬於下面的風險
4帶來的風險:
a 假如某人 發布的 anycpu 控制項庫/Framework 不能被正確的整體編譯,那就是風險。
優雅的介面都隱藏了骯髒的實現,有多骯髒要等真的雲編譯過 發布過拿實際的機器測過才能保證ok的話,測試成本就上去了。
b 如果你用了很深的.net 技巧 比如范基 比如Emit, 這部分的東西在全新環境的輸入和輸出 你需要重新評估
結論:
根據風險評估結果決定用什麼做。
最終會不會和mono一樣連server都搞起 這都是後話,並不是推動這一技術的第一動力
排在第一的 @RednaxelaFX 的答案有一些不精確的地方,在此稍作補充。
關於NGen
準確的說NGen並不是一種完全的AOT技術,它並不能避免在目標機器上JIT編譯代碼,NGen所做的只是在犧牲一部分運行時性能的時候儘可能縮短應用啟動時的預熱時間,而它的手段就是儘可能將代碼預先JIT編譯。NGen與.NET Native相比有一個重要的特點,就是在生產階段不需要任何的妥協,而在接下來的介紹中會發現.NET Native所帶來的巨大性能提升是要付出一部分代價的。首先說一下NGen為什麼不能避免JIT編譯。C#的完全AOT編譯是一項非常複雜,幾乎不可能實現的技術。其中C#所支持的很多動態特性是沒有辦法通過靜態分析完全確定的。例如:- 泛型方法和介面。在NGen生成的Native Image中依然包含泛型方法的JIT Stub,遇到以後是會進行JIT編譯的。
- 主動的動態代碼生成,如正則表達式。C#的正則表達式非常非常地快,原因是.NET的正則表達式在執行前會根據表達式生成一個IL的狀態機並編譯到機器碼。相當於你自己直接用機器碼寫了一個匹配引擎,即使用C語言寫也很難實現這種級別的優化。NGen是不能避免這種主動代碼生成的。
- JIT按代碼的執行順序將IL編譯到機器指令,效果就是經常一起跑的代碼在內存里常常是連續或靠近的,NGen通過Profile Optimization可以模擬類似的效果,所以這點上NGen不會比JIT有優勢。這是JIT編譯的天然屬性,這意味著:
- 更高的CPU緩存命中率
- 更少的Page Fault
- 降低內存使用。IL代碼相比於機器碼體積小非常多,而在JIT的條件下只有真正跑了的機器碼才會放在內存中。
- JIT可以實現跨Library的函數內聯(Cross Assembly Inlining),NGen時不指定排除依賴的話可以對靜態引用實現類似的效果。
- JIT甚至可以根據你的程序上次執行用到哪些函數,在下次啟動時開一個線程提前為你編譯最可能用到的函數(Profile Optimization,不限於NGen)。
所以在NGen的條件下在這幾點上都會有損失,不過可以看到微軟已經試圖通過各種方式讓NGen實現這些JIT才可能的優化了。
說到C比C#快,似乎大家都這麼覺得,其實我覺得在總體性能上,一個精心編寫的C#程序是可以超過同樣的C++程序很大一截的。
首先得明白C#慢在哪。一個C#真正比C++慢的地方是CLR的各種安全性檢查,越界和類型檢查等。雖然不斷優化的JIT編譯器已經會省略一些檢查,但就純粹的「跑」代碼來說,C#是跑不過C++的。但是一個真正懂優化的人一定知道,不是代碼跑的快就是高性能了。前面講的JIT的三個優勢,Locality of Reference,Page Faults和Cross Assembly Inlining,一個真正專業的、寫高性能C/C++程序的工程師一眼就能看明白這意味著怎樣的性能提升。
再說說飽受詬病的GC。首先說從性能優化的角度看為什麼微軟決定使用GC。- 不觸發GC的前提下與malloc相比,向託管堆的內存請求幾乎是瞬間完成的。這也是高性能C/C++程序中相對較慢的操作。
- 分世代的GC使生命周期相近的對象在內存上連續。懂緩存的人應該明白這樣做有多大的優勢。
- 因為對象指針受CLR託管,對象可以在內存中移動,釋放掉的內存碎片可以合併在一起。寫過高性能C++伺服器並且懂得關心對象在內存中的位置的人應該也能明白這是多大的解脫。
如果使用得當,這幾個優勢都是非常明顯和重要的。也是Native語言所謂的GC實現所難以達到的。我一直覺得Native語言是不適合GC的,從蘋果最終採用ARC就能明白,GC的這兩個主要優勢Native語言都是無法實現的。所以微軟自家的C++/CX也採用了ARC的方式,這是正確的。所以不要拿C++的GC跟CLR比較,根本不在一個層面上。
遭人詬病較多的是GC的回收頻率和時間,雖然現在的GC已經可以在後台線程回收內存了,但是在錯誤的使用情景下,GC的掛起時間還是可能非常長。甚至有了C#因為有GC所以就不能寫實時程序的說法。首先要搞清的一點是自動內存管理不代表可以放任不管,也不代表完全失控。
CLR GC的時機是確定的而且可控的,掛起的時間也是可以預測的。CLR的正確打開方式是:- 知道GC在哪些時候會發生,或者那些操作會觸發GC。
- 了解自己的程序中對象的生命周期,長壽短命的都要明確分開。
- 了解自己的程序在哪些時候GC一下沒問題,而在哪些時候絕對不能容忍GC發生。
如果能明確這些,C# GC的開銷對你來說幾乎是可以忽略不計的。如果你不放心別人的代碼,.NET甚至提供API讓你在一段時間內將GC完全禁用。比較典型的是股票交易系統,在交易前強制GC,在交易時間禁用GC,在交易後恢復GC。這樣的精心優化過的程序在性能上是不輸給C++的,而JIT和GC對硬體和內存的充分利用,總體上性能超越C++也並不奇怪。C#比C++慢本身就是一個錯誤的思維定式,這還沒提到.NET Native呢。
.NET Native是一個Runtime
NGen最多算tool chain,而.NET Native是一個不折不扣的Runtime與CLR,CoreCLR是同一類概念。它不是一個精簡版的CLR,而是一個為AOT量身定做的CLR,為此甚至將原來BCL中的一些特性也收納其中。可以說NGen過的只是一個提前編譯的Managed程序,而跑在.NET Native上的是享受著CLR服務的Native程序(不準確,本質上還是Managed)。獲得高性能的同時不犧牲一點生產效率是不可能的,特別是在AOT這個遍地是坑的領域。之前說過NGen沒有任何生產階段的開銷,即原來代碼怎麼寫,現在還怎麼寫,因為它只是一個tool。而.NET Native作為一個平台是需要對現有程序進行遷移的(詳見 Migrating Your Windows Store App to .NET Native )。
其中比較關鍵的:- .NET Native會trim IL代碼,包括你引用的庫的代碼。但是我要反射怎麼辦呢,我要實例化泛型方法怎麼辦呢,你需要在build的時候給一個配置文件,事先說明你要反射什麼,否則會觸發錯誤。
- .NET Native的CLR會包含break change。例如,靜態構造函數的執行順序,API返回對象的實際類型。這些都可能讓你的程序不正常。
- .NET Native的線程模型有改變。例如在一個線程上Spin可能導致所有線程都掛起。例如各類死鎖和無限遞歸。這不是bug,這是AOT領域不得不面對的現實,有如Mono Touch上的ExecutionEngineException,你不可能獲得一切之後還能獲得AOT,不是技術水平問題。
所以可以看出來,說能達到C#的生產效率也只是相對的,還是要付出代價的。既然如此,微軟不惜Break掉現有App來推.NET Native的動機是什麼呢。
答案很明顯是App的效能問題。微軟推出了查克拉引擎和C++/CX,資源使用情況都要好於.NET。只是.NET + C# + XAML的開發架構實在太過熟悉和高效,遷移到新的Native平台上的抵觸是很明顯的。而我個人覺得不得不做.NET Native的硬傷是WinRT Interop。
做過Windows App的人知道這個平台都是基於Windows Runtime的,WinRT宏觀層面是一套ABI。這樣就可以支持基於查克拉的JavaScript,基於Native的C++/CX和基於.NET的C#之間的互操作。但是談到ABI就會涉及到互操作的性能問題。尾巴很大的.NET是性能最堪憂的,為了調用一下WinRT,它需要讀取解析winmd元數據,生成互操作的方法,然後JIT編譯,最後再調用和封送數據。為了支持非同步編程模式,也需要在兩個Runtime間頻繁轉換數據。已經到了看著都肉疼的程度了。
而.NET Native可以將AOT編譯過的C#程序直接和WinRT鏈接到一起,從根本上改善了這個問題。關於.NET Native的前景,我個人的感覺短時間內很難推廣到桌面或伺服器技術棧。因為我認為桌面和伺服器程序並不適合.NET Native,它們的未來應該在CoreCLR,並與.NET Native共享.NET Core 5。
.NET Native的性能提升- 從二進位層面刪掉不用的代碼。App可以擁有更小的memory footprint。
- 沒有了絕大部分JIT編譯,App啟動時間明顯降低。
- 所有代碼鏈接到一個Native Image上,C++級別的代碼執行優化,但是跟JIT相比實際效果如何要具體分析。
- 數據序列化更高效,object graph不需要反射,預先編譯,ApplicationData會更快,進一步加速App啟動。
雖然伺服器程序上C#很容易超過C++,開發App方面卻是比較難比較的,主要看GC的運用。精心優化達到同等水平是很有可能的。當然,不管在什麼場景下,C#的性能都是沒辦法超過極端優化過的C/C++程序,但我覺得在對優化同等程度的投入下,不管是不是靠AOT,優秀的C#工程師都是可以寫出比C++程序更綜合綜效的系統來的。
-本人也寫C++,還望C++高手們不喜勿噴。@RednaxelaFX的答案不錯, 我去.NET Native看了一下,做了大量優化,不僅僅是啟動優化。
Microsoft .NET Native
====================================================================
Native只是優化啟動過程,對整體沒有大的改善。像.net, java這種託管代碼,效率永遠不可能達到c/c++的水平。因為他們在運行時會做許多檢測,如數組坐標是否溢出,null引用方法調用等等,這些c/c++根本不做,交給程序員自己解決。這也是為什麼c#圖形處理用unsafe,在裡面使用指針提高效率的原因。第一,這類問題最好的回答是測試。
第二,如何寫好一個科學的測試。用一個項目在不同平台上的實現來比較,是垃圾測試,因為複雜度大,應用的庫不同,導致得出的數據沒有意義。好的做法,是對單項基本運算在不同平台下進行比較。比如,字元串操作,整數運算,浮點運算,內存分配等。第三,有人做過單項的測試,c++並非在所有單項上都能領先。這類問題其實是偽問題。假如基本要素差不多,比如數據類型相似,都有比較完整的數據類型,用哪種語法,並不足以造成很大的性能差別。編譯器實現的好壞才會造成差距。有些比較簡單的腳本語言,沒有複雜的數據類型,會造成一定影響。所以結論是,可以忽略c#跟c++性能差距這類問題,放心用。c/c++相對於java c#的快,很多人都理解是因為虛擬機的導致的比如cpu運算之類的問題。卻很大的忽略了。c/c++最擅長的內存管理,合理的使用池去管理內存對象比java/c#不停的開銷回收對象要快很多很多~~習慣了垃圾回收編程的人就算去寫本地代碼那又怎樣呢。。呵呵→_→
~~~關於內存的管理,虛擬機會再次從物理內存進行拷貝或者移動,所以虛擬機在內存處理上沒有直接操作內存快。不要盲目相信各種商業公司的宣傳。 等微軟完全用C#重寫所有產品的時候再用也不遲。
經過下面知友親切友好的評論覺得之前 自己確實不夠了解一些概念,答非所問.簡單查閱了下資料,下面直接引用一下:@陳宇 如何評價微軟推出的 .NET Native? - 知乎用戶的回答
Java的出現,讓人們知道了,現在的計算機足夠快,損失一點運行效率,換取開發效率,是有意義的。於是微軟出了.NET,Android也是基於Java的。
iOS
的經驗,又告訴我們,編譯到機器碼可以省電,可以提高流暢度,是有意義的。特別是對移動平台,.NET和Java如果用即時編譯,即耗時間又占內存,不能
提供足夠好的用戶體驗。於是,微軟先做了Ngen雲編譯又出了這個.NET Native來回應,安卓平台也在4.4版加入了ART。主要廠商在這方面還是有共識的,大家殊途同歸。.NET的預編譯Ahead Of Time Compilation/AOT,在開源的Mono里實現了挺長時間了,但是沒有太多關注。這次微軟的.NET Native有兩個亮點,看起來還是很有希望的推開的。一個是雲編譯。也就是開發者提交的是虛擬機代碼,用戶直接下載自己的設備可用的機器碼。不像Mono要手動編譯,這對用戶和開發者來說,都很方便。同樣的策略,微軟之前在WP8上基於Ngen實施過,看起來效果不錯。.NET Native會把它擴大到了所有AppStore應用,讓更多人享受到這個改進。另一個是,因為速度、空間的限制都不存在了,雲編譯器可以比即時編譯JIT,比ART的安裝時編譯,做更多更好的優化。微軟的Visual C++做了這麼多年,應該有足夠的技術儲備。....把用庫連進來,可以做更多全局優化,比如鏈接時優化Link Time Optimization /
LTO了。早期的C/C++編譯器,生成的目標文件、庫文件之間不能做優化處理,後來很多編譯器做了LTO。把小函數調用inline嵌入進來,再做各種
窺孔分析局部優化。看來.NET也要有這種優化了,這對所有平台,不管是伺服器、桌面還是移動系統,都有加速效果。(考慮到開發者調試的需要,而且企業內的應用程序也不方便雲編譯,.NET Native將來應該也會支持本地編譯,就像Ngen被包含在.NET Framework4.5里一樣)
.NET直接編譯成本地代碼:.NET Native架構簡介
What is the difference between .NET Native and Ngen.exe?這裡簡單講一下.NET Native的基本架構
...
.NET
Native和之前的NGEN有本質區別。NGEN實際上是把CLR運行時的數據結構和代碼給一鍋端(當然,這個是簡化的說法,實際上比這個複雜)的放到
最終的PE裡面去了,運行的時候還是需要整個.NET Framework支持,而且不能避免JIT。.NET Native是全新的技術,整個.NETFramework經過refactoring重寫,最終的runtime非常小,只有數百K,無需任何安裝(除了mrt100.dll之外,大家可以理
解成msvcrt.dll)。大部分的功能都從runtime中refactor到framework中作為C#代碼或者作為toolchain一部分存
在。一個典型的例子是:P/invoke原來是由CLR實現,使用C++和彙編編寫。而現在是經由MCG這個工具接手,直接生成C#。最終.NETNative生成的EXE/DLL是可以直接運行的機器碼(通過C++編譯器後端生成)。對了,有些朋友可能會問:我們是不是直接生成C++代碼?答案是
否定的。我們所使用的C++編譯器後端接受IL作為輸入,生成MDIL。幾個我聽到的常問的問題(如果有些答案過於「官方」,請諒解):
1. 你們最後是生成C++代碼嗎?
答:不生成。C++後端直接從IL轉換成MDIL。
2. WPF支持嗎?
答:暫時不支持。目前暫時只支持Windows Store Apps
3. 這個會支持桌面程序嗎?
答:目前暫時只支持Windows Store Apps
4. 這個支持JIT嗎?
答:目前.NET Native不支持JIT,所有代碼都是編譯時候生成。
5. 既然是本地機器碼,為什麼還可以支持類型反射(reflection)?
答:機器碼和反射並不衝突,我們在PE文件中儲存了額外的用於反射的信息,然後動態讀取此信息進行調用。C++也可以支持反射(RTTI),只是不如.NET強大而已。
6.這個需要安裝.NET Framework嗎?
答:開發編譯的時候需要,運行時不需要。
7.為什麼不支持VB
答:VB本質上和C#都是生成IL,技術上非常類似。只是目前我們因為時間問題,暫只支持C#。
8.為什麼啟動運行速度會變快?
答:一方面歸功於C++的優秀的編譯器後端,一方面也因為runtime的重寫和簡化。
You can think of .NET Native as an evolution of the NGen technology
that the desktop CLR uses. There"s a few major ways that .NET Native and
NGEN differ -
- Runtime dependency - NGEN uses the full desktop
CLR, .NET Native uses a refactored runtime (mrt100_app.dll) which is
application local. The .NET Native runtime has been refactored to move
most functionality out of the application and into the code generation
tool chain. This makes it much smaller, more pay for play, and
(hopefully) more debuggable at runtime. A .NET Native application is
also self-contained, which is a useful property to have for an
application.- Native image dependencies - an NGEN image is
tightly bound to both the CLR that it runs against and the NGEN image of
its dependent assemblies. This, for example, causes nearly all NGEN
images to need to be regenerated when a bug fix is made to mscorlib.dll.- Compilation Location - the aim for .NET Native is
to have the native code be generated in the app store. NGEN generates
native code on the end user device. You can certainly imagine that for
certain classes of devices (ie phones, tablets) you"d much rather not
waste end-user battery life generating code. Compiling in the store also
allows .NET Native to spend more time compiling, therefore allowing it
to apply more optimizations than NGEN can afford.- Code generator - NGEN uses the JIT compiler to
generate code, .NET Native uses the Visual C++ compiler"s back end,
which enables us to apply optimizations such as auto-vectorization that
are too expensive to apply in the JIT case- Whole program analysis - NGEN generates code for a
single assembly at a time, which allows an NGEN image to be used in
multiple application contexts. .NET Native generates code for an entire
application package, which allows it to apply a broader set of
optimizations (for instance, entirely throwing away code that"s not ever
used at runtime). This is coupled with a refactored framework which
enables these optimizations to kick in as much as possible.- IL Fallback - NGEN images contain both the native
code and MSIL for an assembly (among other data structures). If
something occurs at runtime which causes the CLR to need native code
that it cannot find in the NGEN image, it can fall back to JITing. In
.NET Native"s current developer previews, only native code is present in
the native image. This means that if the code isn"t present in the
image, it will never execute at runtime.
其他:
1.7 Native代碼產生器: NGen.exe(Native Image Generator)
隨.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文件中的編譯好的代碼, 文件中的函數就不需要在運行時刻編譯了.
在表面上, 這聽起來非常好, 聽上去就像如果你得到了託管代碼的全部優點(垃圾回收, 代碼驗證, 類型安全, 等等)而不犧牲託管代碼的性能(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開發者對圍繞基地址和重定位的問題很熟悉, 關於這個主題的更多信息, 可以參考我的書」programming Applications for Microsoft Windows, 4th Edition」. 當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編譯器就會被載入, 應用程序的工作集將會增加.
Android runtime
ART 的機制與 Dalvik 不同。在Dalvik下,應用每次運行的時候,位元組碼都需要通過即時編譯器轉換為機器碼,這會拖慢應用的運行效率,而在ART 環境中,應用在第一次安裝的時候,位元組碼就會預先編譯成機器碼,使其成為真正的本地應用。這個過程叫做預編譯(AOT,Ahead-Of-Time)。這樣的話,應用的啟動(首次)和執行都會變得更加快速。
也許我這麼說有點過,但實際上.Net Native和NGen、ART的實現思路完全不同,反倒和Go語言有異曲同工之妙,如果做的好的話,在某些情況是可以達到甚至超過100% C++的性能的,Go已經證明了這點,在語言的易用性上我想一個TPL庫就夠喝一大壺了,至少我個人來說很喜歡.Net Native想要達到的目標
可以達到C#的生產效率,但是性能只能接近C++,不太可能達到。
感覺跟go很像,轉了一圈又回去了,哲學家說的不錯,歷史總是螺旋上升的。
.net native 雖然大家都期待,但是事實還是讓不少人大失所望
因為MS的目標和大家的期望並不是一致的。大家可能希望C#寫的程序能夠像C++的程序一樣。但是可惜。實際上C#的目標只不過是 AOT的高級版 實在是算不上真正徹底的 native。再者 不能摒棄C++的話 MS勢必永遠擺脫不了C++的魔爪了。C#本身越發臃腫。即使native了,也不可能替代C++。就算MS坐擁iphone這樣的成就也不可能。
反正 C#的發展 我看是基本到頂峰了,對,不會再有新的高度了,或者也可以說開始走下坡路了。
C#如果不能大刀闊斧得改革,以後估計也沒什麼機會了。畢竟,使用場景已經慢慢被其他平台代替。服務端領域有 java 挑大樑,go, C++ 和 C,Web領域有python,ruby,php,移動領域有java,oc,swift,C++,底層和嵌入式有 C,C++,java,怎麼也輪不到C#了。C# 以後還能在哪裡大放異彩呢?顯然 沒什麼機會了。
放眼望去 也只有在 unity 上還在流行了,而且還是被動選擇(要不是因為 unity,C#現在的使用率可以想像是怎樣一種情況)。顯然 native 之路不僅僅是晚十五年的問題,而是這條路已經偏離了路線,因為這語言的目標已經不是程序員們想要的那樣,MS繼續想當然,自以為是地開始為程序員們做需求了,當然你們知道MS的需求做的一直都是很糟糕的,WP,Metro,等等 不計其數。。。
儘管14年剛聽說的時候我可能還有點興趣,但是現在我對.net native 持悲觀態度,兩年了 .net native 給我們帶來了什麼? 雞肋之勢 已成!呃,這不就是Golang的套路么?
很有現實意義。不過不管怎麼玩,.NET 的使用範圍依舊沒有太大的改變,不會去佔領C++ 的份額太多。
推薦閱讀:
※.Net core現在可以做什麼?未來發展有前景嗎?
※如何評價 JetBrains .NET IDE 的正式版 Rider 2017.1 ?
※VS里有什麼Eclipse里沒有的強大功能?
※如何編寫能夠監聽特定程序或全系統所有Http請求的.Net程序?
※C#為什麼Random類不做靜態?
TAG:微軟Microsoft | NET | C | C# | NETNative |