在軟體開發領域,最佳化效能和簡化效率仍然至關重要。多年來,.NET 平臺一直在創新,為開發人員提供基礎設施,以打造彈性和高效的軟體解決方案。今天要寫的這篇文章源自昨天在朋友圈發的文章《UWP 透過 .NET 9 和Native AOT 的支援實現 UWP 應用的現代化》[1],一位小夥伴的對話讓我想全面梳理下Native AOT的現在的進展。
什麼是 .NET Native AOT?
.NET Native Ahead-of-Time (AOT) 編譯是 .NET 平臺中的一項前沿進步。使用 AOT 時,C# 程式碼在開發人員計算機上被編譯為本機程式碼。這與在執行時將程式碼編譯為本機程式碼的傳統方法形成鮮明對比。
下面的架構說明了這一點。.NET 傳統編譯涉及兩個步驟:
- C# 編譯生成包含中間語言 (IL) 程式碼的 DLL 檔案。此類 DLL 稱為 .NET 程式集。
- 執行 .NET 程式時,.NET 執行時(CLR 公共語言執行時)將載入 .NET 程式集。CLR 的子系統負責將 IL 程式碼編譯為由 CPU 直接執行的本機程式碼。此子系統稱為 JIT (Just-In-Time) 編譯器。它之所以得名,是因為它僅在首次呼叫該方法時編譯該方法的 IL 程式碼。
另一方面,.NET Native AOT 編譯由一個步驟組成。將 C# 原始碼編譯為開發人員計算機上的本機程式碼。此過程包括將 C# 程式碼轉換為 IL 程式碼,然後轉換為 Native 程式碼,形成一個兩步編譯過程。但這是一個實現細節。這就是 AOT .NET 程式集 框在下面的架構中為灰色的原因。
.NET Native AOT 的優勢
.NET Native Ahead-of-Time (AOT) 編譯帶來了一系列優勢:
- 增強的效能:透過將程式碼預編譯為本機計算機指令,.NET Native AOT 顯著縮短了啟動時間並提高了應用程式的整體效能。在無伺服器方案中,如果應用程式針對每個請求啟動,這可能會產生重大差異。此外,執行時沒有 JIT 編譯開銷,這意味著執行速度更快,從而提供更流暢的使用者體驗。
- 簡化部署:AOT 編譯的應用程式通常會導致依賴項為零或較少的獨立可執行檔案。這簡化了部署過程,可以更輕鬆地在各種平臺和裝置之間分發應用程式,而無需額外的安裝或執行時元件。
- 更小的應用程式大小:透過修剪不必要的程式碼,AOT 可以大大減小應用程式的大小。這不僅可以節省儲存空間,還可以最佳化應用程式的記憶體佔用,這在移動裝置或 IoT 裝置等資源受限的環境中尤為重要。
- 增強的智慧財產權保護:AOT 編譯將原始碼轉換為最佳化的機器程式碼,這使得逆向工程嘗試更具挑戰性。生成的本機程式碼比 IL 程式碼更加模糊,並且難以破譯,因為 IL 程式碼可以輕鬆反編譯為原始 C# 程式碼。這增強了應用程式中嵌入的敏感演算法、業務邏輯和專有方法的安全性。
.NET Native AOT 的缺點
使用 AOT 獲得的好處不可避免地伴隨著某些缺點。他們是:
- 特定於平臺的編譯:.NET Native AOT 生成特定於平臺的本機程式碼,針對特定體系結構或作業系統進行定製。例如,與常規 .NET 程式集不同,在 Windows 上使用 AOT 生成的可執行檔案在 Linux 上不起作用。
- 不支援跨 OS 編譯。例如,在 Windows 機器上,您無法編譯 Linux 本機版本,反之亦然。
- 對 Reflection 的部分支援:反射依賴於動態程式碼生成和執行時型別發現,這與 AOT 編譯程式碼的預編譯和靜態性質相沖突。但是,通常的 Reflection 用法與 AOT 配合得很好。
- 需要 AOT 相容的依賴項:AOT 編譯要求專案中使用的所有庫和依賴項都與 AOT 相容。依賴於反射、執行時程式碼生成或其他動態行為的庫可能與 AOT 不相容,這可能會導致衝突或執行時錯誤。
- 增加構建時間:AOT 編譯涉及在構建過程中預先生成本機程式碼。這個額外的步驟會顯著增加構建時間,特別是對於大型專案或具有大量程式碼庫的應用程式。
- 需要適用於 C++ 的桌面開發工具:AOT 只能在安裝這些工具的情況下進行編譯,這些工具在您的硬碟驅動器上最多可重 7GB。
.NET 9 的 Native AOT(Ahead-of-Time Compilation)是微軟在.NET 9版本中重點發展的一項技術,旨在提升應用程式的效能。Native AOT 技術可以將.NET 程式碼預先編譯為原生代碼,從而實現更快的啟動時間和更高的執行效率。
在.NET 9中,微軟已經將Native AOT作為提升效能的關鍵點之一[2]。此外,微軟還宣佈了對通用Windows平臺(UWP)的初步支援,允許開發者使用.NET 9和Native AOT技術來現代化改造現有的UWP應用。這一舉措為UWP開發者提供了一條升級路徑,使他們能夠利用最新的.NET和Native AOT技術來改進其應用程式。 .NET 9的Native AOT不僅限於UWP平臺,它還支援老舊的Windows 7和XP環境,這標誌著AOT技術在相容性方面的突破。然而,對於Android平臺的Native AOT支援,目前尚未完成,尤其是JNI(Java Native Interface)支援,這被認為是一個較大的功能需求,還有WPF/Winform 的Native AOT支援也需要在.NET 10 才能夠完成。
.NET 的Native AOT技術透過在編譯階段對程式碼進行最佳化,使得生成的可執行檔案更小且啟動速度更快。這一突破性功能不僅實現了對老舊Windows 7和Windows XP環境的支援,還為效能要求高且依賴舊版系統的開發者提供了新的可能性。具體來說,.NET 9版本中,為了確保向下相容性,X86架構下的AOT(Ahead-of-Time)編譯器的支援擴充套件,.NET 9採用了精心設計的編譯策略,確保了對Win7及XP API的相容性,使程式碼能夠無縫執行,允許開發人員將應用程式在編譯階段就最佳化為能夠在老舊的Windows系統上執行。LoongArch架構和Risc-V架構下的AOT 編譯器支援,社群也在繼續完善之中。
在.NET 9中,對Native AOT(按需編譯)支援和JNI(Java Native Interface)的支援有以下具體進展:
- 在.NET 9中,引入了兩個新的屬性,允許開發者設計功能開關。這些功能開關可以在.NET庫(以及你自己)中使用來切換某些功能區域。如果一個功能不被支援,在裁剪或使用Native AOT進行編譯時,將移除那些不受支援且不必要的功能,從而減小應用程式的大小。
- 在.NET MAUI的測試中,透過呼叫JNI來獲取Java陣列元素的效能比使用string.Split和新的Span方法更差。這表明開發者正在考慮如何在未來版本中最佳化這一過程。
- .NET 9在Android平臺上對Native AOT的支援主要體現在透過新屬性實現的功能開關,以及透過Native AOT減少應用大小的能力。
對於開發者而言,學習和採用.NET 9的Native AOT技術需要具備以下前置知識或技能:
對.NET平臺的理解:首先,開發者需要對.NET平臺有基本的瞭解,包括其架構、執行時環境以及如何在不同平臺上部署應用。這有助於理解Native AOT技術如何與現有.NET生態系統整合。
熟悉C#或F#程式語言:由於.NET 9支援透過C#或F#進行開發,因此掌握這些程式語言是必要的。此外,瞭解這些語言的高階特性將有助於更有效地利用Native AOT帶來的效能優勢。
瞭解編譯器原理:Native AOT(Native Application Optimized Translation)是一種預編譯技術,它允許開發者直接生成機器碼而不是依賴JIT(即時編譯)。因此,對編譯器的工作原理有一定的瞭解可以幫助開發者更好地理解和使用Native AOT技術[3]。
效能最佳化經驗:由於Native AOT旨在提供可預測的效能並減少資源消耗,因此具備一定的效能最佳化經驗是有益的。這包括對記憶體管理、程式碼最佳化等方面的深入瞭解。
雲原生和微服務架構知識:雖然不是必須的,但瞭解雲原生應用和微服務架構的相關知識可以增強開發者在使用.NET 9時構建高效、可擴充套件的應用的能力。這是因為.NET 9特別強調了在這些架構中的效能表現,參見.NET 9 RC1釋出[4]。
隨著 .NET 的不斷髮展,我們可以期待更多的庫和框架支援Native AOT,這使其成為希望最佳化其應用程式的開發人員更具吸引力的選擇。那麼為什麼不試一試,看看你能在多大程度上提升你的應用的效能呢?
相關連結:
- [1]UWP 透過 .NET 9 和Native AOT 的支援實現 UWP 應用的現代化:https://mp.weixin.qq.com/s/lCgDOeaTuwmGagZWZG2AmQ
- [2]在.NET 9中,微軟已經將Native AOT作為提升效能的關鍵點之一: https://www.cnblogs.com/shanyou/p/18015105
- [3]Native AOT技術指南: https://www.cnblogs.com/hez2010/p/17999838/guidance-for-dotnet-nativeaot
- [4].NET 9 RC1釋出: https://mp.weixin.qq.com/s/WabCosc39FlI3Wrylf7CbQ