ZOMI的AI編譯原理1

真真夜夜發表於2024-04-09

感謝up主 ZOMI醬:https://space.bilibili.com/517221395
《AI編譯器開發》沒點基礎還真看不懂

01 編譯器基礎概念

  1. 什麼是編譯器?
  2. 為什麼 AI 框架需要引入編譯器?
  3. AI 框架和 AI 編譯器之間什麼關係?

編譯器與直譯器

編譯器(Compiler)和直譯器(Interpreter)是兩種不同的工具,都可以將程式語言和指令碼語言轉換為機器語言。雖然兩者都是將高階語言轉換成機器碼,但是其最大的區別在於:直譯器在程式執行時將程式碼轉換成機器碼,編譯器在程式執行之前將程式碼轉換成機器碼

JIT和AOT編譯方式

目前,程式主要有兩種執行方式:靜態編譯和動態解釋

  • 靜態編譯的程式碼程式在執行前全部被翻譯為機器碼,通常將這種型別稱為 AOT(Ahead of time),即“提前編譯”;
  • 動態解釋的程式則是對程式碼程式邊翻譯邊執行,通常將這種型別稱為 JIT(Just in time),即“即時編譯”。

AOT 程式的典型代表是用 C/C++ 開發的應用,其必須在執行前編譯成機器碼,然後再交給作業系統具體執行;而 JIT 的代表非常多,如 JavaScript、Python 等動態解釋的程式。

特點 JIT (即時編譯) AOT (提前編譯)
優點 1. 可以根據當前硬體情況實時編譯生成最優機器指令
2. 可以根據當前程式的執行情況生成最優的機器指令序列
3. 當程式需要支援動態連結時,只能使用JIT的編譯方式
4. 可以根據程序中記憶體的實際情況調整程式碼,使記憶體能夠更充分的利用
1. 在程式執行前編譯,可以避免在執行時的編譯效能消耗和記憶體消耗
2. 可以在程式執行初期就達到最高效能
3. 可以顯著加快程式的執行效率
缺點 1. 編譯需要佔用執行時Runtime的資源,會導致程序執行時候卡頓
2. 編譯佔用執行時間,對某些程式碼編譯最佳化不能完全支援,需在流暢和時間權衡
3. 在編譯準備和識別頻繁使用的方法需要佔用時間,初始編譯不能達到最高效能
1. 在程式執行前編譯會使程式安裝的時間增加
2. 將提前編譯的內容儲存起來,會佔用更多的記憶體
3. 犧牲高階語言的一致性問題

在AI框架中區別

目前主流的 AI 框架,都會帶有前端的表達層,再加上 AI 編譯器對硬體使能,因此 AI 框架跟 AI 編譯器之間關係非常緊密,部分如 MindSpore、TensorFlow 等 AI 框架中預設包含了自己的 AI 編譯器。目前 PyTorch2.X 版本升級後,也預設自帶 Inductor 功能特性,可以對接多個不同的 AI 編譯器。

編譯方式 描述 典型代表
AOT (提前編譯) 靜態編譯的程式碼程式在執行前全部被翻譯為機器碼,適合移動、嵌入式深度學習應用。 1. 推理引擎:訓練後的AI模型提前固化,用於推理部署。
2. 靜態圖生成:神經網路模型表示為統一的IR描述,執行時執行編譯後的內容。
JIT (即時編譯) 動態解釋的程式邊翻譯邊執行,適合需要實時最佳化的場景。 1. PyTorch JIT:將Python程式碼實時編譯成本地機器程式碼,最佳化和加速深度學習模型。
2. Jittor:基於動態編譯JIT,使用元運算元和統一計算圖的深度學習框架,實現高效操作和自動最佳化。

Pass 和中間表示 IR

Pass 主要是對源程式語言的一次完整掃描或處理。在編譯器中,Pass 指所採用的一種結構化技術,用於完成編譯物件(IR)的分析、最佳化或轉換等功能。Pass的執行就是編譯器對編譯單元進行分析和最佳化的過程,Pass 構建了這些過程所需要的分析結果。
img

在編譯器 LLVM 中提供的 Pass 分為三類:Analysis pass、Transform pass 和 Utility pass

Pass 型別 描述 功能 常見例子
Analysis Pass 計算相關IR單元的高層資訊,但不對其進行修改。這些資訊可以被其他Pass使用,或用於除錯和程式視覺化。 1. 從IR單元中挖掘並儲存資訊
2. 提供查詢介面供其他Pass訪問
3. 提供invalidate介面以處理資訊失效
Basic Alias Analysis、Scalar Evolution Analysis
Transform Pass 使用Analysis Pass的分析結果,以某種方式改變和最佳化IR。 1. 改變IR中的指令和控制流
2. 可能會減少函式呼叫,暴露更多最佳化機會
Inline Pass
Utility Pass 功能性的實用程式,不屬於Analysis Pass或Transform Pass。 1. 執行特定任務,如提取basic block extract-blocks Pass

IR(Intermediate Representation)中間表示,是編譯器中很重要的一種資料結構。編譯器在完成前端工作以後,首先生成其自定義的 IR,並在此基礎上執行各種最佳化演算法,最後再生成目的碼。


如圖所示,在編譯原理中,通常將編譯器分為前端和後端。其中,前端會對所輸入的程式進行詞法分析、語法分析、語義分析,然後生成中間表達形式 IR。後端會對 IR 進行最佳化,然後生成目的碼
img
例如:LLVM 把前端和後端給拆分出來,在中間層明確定義一種抽象的語言,這個語言就叫做 IR。定義了 IR 以後,前端的任務就是負責最終生成 IR,最佳化器則是負責最佳化生成的IR,而後端的任務就是把 IR 給轉化成目標平臺的語言。LLVM 的 IR 使用 LLVM assembly language 或稱為 LLVM language 來實現 LLVM IR的型別系統,就指的是 LLVM assembly language 中的型別系統。

因此,編譯器的前端,最佳化器,後端之間,唯一交換的資料結構型別就是 IR,透過 IR 來實現不同模組的解耦。有些IR還會為其專門起一個名字,比如:Open64的IR通常叫做WHIRL IR,方舟編譯器的IR叫做MAPLE IR,LLVM則通常就稱為LLVM IR。
img

IR 在通常情況下有兩種用途,1)一種是用來做分析和變換,2)一種是直接用於解釋執行

編譯器中,基於 IR 的分析和處理工作,前期階段可以基於一些抽象層次比較高的語義,此時所需的 IR 更接近原始碼。而在編譯器後期階段,則會使用低層次的、更加接近目的碼的語義。基於上述從高到低的層次抽象,IR 可以歸結為三層:高層 HIR、中間層 MIR 和 底層 LIR。

IR 型別 描述 用途 特點
HIR (High IR) 基於源程式語言執行程式碼的分析和變換。 用於IDE、程式碼翻譯工具、程式碼生成工具等,執行高層次程式碼最佳化(如常數摺疊、內聯關聯)。 1. 準確表達源程式語言的語義
2. 可以使用AST和符號表
MIR (Middle IR) 獨立於源程式語言和硬體架構執行程式碼分析和最佳化。 用於編譯最佳化演算法,執行通用最佳化(如算術最佳化、常量和變數傳播、死程式碼刪除)。 1. 與源程式程式碼和目標程式程式碼無關
2. 通常基於三地址程式碼(TAC)
LIR (Low IR) 依賴於底層具體硬體架構做最佳化和程式碼生成。 用於執行與具體硬體架構相關的最佳化,生成機器指令或彙編程式碼。 1. 指令通常與機器指令一一對應
2. 體現了具體硬體架構的底層特徵

三地址程式碼 TAC 的特點:最多有三個地址(也就是變數),其中賦值符號的左邊是用來寫入,右邊最多可以有兩個地址和一個運算子,用於讀取資料並計算。

多層 IR 和單層 IR 比較起來,具有較為明顯的優點:

  • 可以提供更多的源程式語言的資訊
  • IR表達上更加地靈活,更加方便最佳化
  • 使得最佳化演算法和最佳化Pass執行更加高效

如在 LLVM 編譯器裡,會根據抽象層次從高到低,採用了前後端分離的三段結構,這樣在為編譯器新增新的語言支援或者新的目標平臺支援的時候,就十分方便,大大減小了工程開銷。而 LLVM IR 在這種前後端分離的三段結構之中,主要分開了三層 IR,IR 在整個編譯器中則起著重要的承上啟下作用。從便於開發者編寫程式程式碼的理解到便於硬體機器的理解。
img

總結

  1. 直譯器是一種計算機程式,將每個高階程式語句轉換成機器程式碼
  2. 編譯器把高階語言程式轉換成機器碼,即將人可讀的程式碼轉換成計算機可讀的程式碼
  3. Pass 主要是對源程式語言的一次完整掃描或處理
  4. 中間表示 IR 是編譯器中的一種資料結構,負責串聯起編譯器內各層級和模組

相關文章