【底層】 C++和C#的編譯方式差異 / AOT和JIT

JimmyZou發表於2024-08-18

什麼是EXE檔案?

EXE是二進位制的、可移植可執行 (PE)的檔案,它包含程式的機器程式碼、資源和後設資料。

具體檔案結構如下:

  1. 檔案頭
    描述了該檔案的型別、大小、節段(sections)等資訊。

  2. 節段
    程式碼段(.text):包含實際的機器指令(可執行程式碼)或中間語言IL程式碼。
    資料段(.data):儲存靜態資料(如全域性變數和常量)。
    資源段(.rsrc):包含應用程式使用的資源,如圖示、選單等。
    重定位資訊(.reloc):當程式需要載入到非原始地址時,重定位資訊幫助調整程式碼和資料的位置。

  3. 匯入表和匯出表
    列出該程式所依賴的其他庫(如 DLL),以及使用的具體函式。

AOT和JIT的區別

  • AOT(Ahead of Time):提前編譯。在程式執行前就編譯成機器碼或者中間語言IL,存放到檔案系統中。

  • JIT(Just in Time):即時編譯。將中間語言IL在執行時編譯成機器碼。有點像解釋,但不完全是。

C#的編譯執行方式

(1) 原始碼先被編譯成中間語言IL,放入.exe檔案的程式碼段中(.text段)中。

(2) exe檔案在雙擊執行的時候,呼叫CLR(該clr.dll依賴位於.Net Framework資料夾下,見下圖),它使用即時編譯JIT來將IL動態編譯成機器碼,執行具體操作。

圖片名稱

C++的編譯執行方式

原始碼直接透過編譯器編譯得到最後的.exe中的機器碼,雙擊直接執行。(整個從原始碼到exe的過程中實際還有預處理、彙編、連結,因為我主要是側重JIT的差異部分展開對比,所以這些步驟省略沒有提)

圖片名稱

為什麼C#要多一個即時編譯JIT的步驟?

  • 跨平臺性:C# 預先編譯成 IL 程式碼,然後只需在相應平臺上實現相應的 CLR,就可以跨平臺執行。

  • 易於管理:JIT階段需要由CLR管理執行,它提供垃圾回收、異常處理、執行緒管理等服務,簡化了開發者的工作。

  • 多語言支援:不管語言是什麼,如果都編譯成 IL,CLR就可以統一呼叫(CLR只認識IL)。

什麼又是Mono和IL2CPP?

Mono

  • 定義: Mono 是一個開源的跨平臺 .NET 框架的實現,最初由 Xamarin 開發,旨在允許 C# 程式在多種平臺(包括 Windows、Linux 和 macOS)上執行。

  • 功能:提供了 CLR 的某些功能,並實現了 C# 編譯器、類庫和執行時。

  • 與 CLR 的關係:Mono 實現了 .NET 的一些核心功能,作為一個替代 CLR 的執行時環境,同時支援執行同樣的 IL 程式碼。

IL2CPP

  • 定義: IL2CPP(Intermediate Language To C++)是 Unity 引擎原生的一部分,將 C# 中間語言程式碼轉換為 C++ 程式碼,然後編譯成機器碼,適用於各種平臺。
  • 功能: 提供更高效的執行時效能和較強的安全性,特別是在移動和遊戲開發中。
  • 與 CLR 的關係: IL2CPP 不直接使用 CLR,而是將 IL 轉換為 C++ 程式碼,繞過 CLR 的執行時環境,從而實現更好的效能和跨平臺支援。

兩者的優缺點和現狀

  • (缺)Mono是.Net的一種實現,它會用CLR,也就是會呼叫JIT,但IOS上不支援JIT。

  • (缺)Mono有版許可權制,用不了C#的一些新特性。

  • (缺)Mono雖然跨平臺,但是是需要每個平臺單獨實現了對應CLR,所以有幾個平臺就得寫幾種Mono虛擬機器,比較費事。

  • (優)Mono構建速度快一些(不是指執行),因為JIT是執行時用到什麼才把什麼轉換成機器碼的,CLR中也有一些記憶體管理的方法,會快很多。

  • (優)IL2CPP把IL變成C++,各個平臺本身就是支援C++的。

  • (缺)IL2CPP構建慢一些,因為要提前把所有的程式碼轉換成C++。不過也只是個小問題。

  • (優)IL2CPP執行快,因為最後的機器碼是透過C++得到的。

相關文章