(浙江大學編譯原理課程的課程報告)
前言
並行性
是指計算機系統具有可以同時進行運算或操作的特性,在同一時間完成兩種或兩種以上工作。並行性等級可以分爲作業級或程序級
、任務級或程序級
、指令之間級
和指令內部級
。
對於在一個具有指令級並行機制的處理器上程序的並行能力,需要考慮以下因素:
- 程序中潛在的並行性,或者說程序中預算之間的依賴關係;例如具有簡單的控制結構和規則的數據訪問模式的數值應用中的並行性就相對較多;
- 處理器上可用的並行性,比如可以用以計算的硬件資源的數目;
- 從原來的順序程序中抽取並行性的能力;
- 在給定的指令調度約束下找到最好的並行調度方案的能力;
並行性抽取和並行執行的調度可以通過軟件靜態完成,也可以通過硬件動態完成,或二者互相結合。編譯中主要涉及的就是軟件相關的靜態過程,即如何通過在編譯的過程中進行指令抽取
和指令調度
,來達到更好的並行性和運行速度。
本文希望從並行性相關的處理器體系結構實現、基本塊調度算法、全局調度算法等方面來介紹編譯過程中的並行性問題。
並行相關的處理器體系結構
並行性的基礎是現代高性能處理器的硬件能夠在一個時鐘週期能執行多條指令。現流行的並行技術大都可以從三個方面實現:資源重複、資源共享、時間重疊。其中主要應用到的技術如下:
-
流水線技術
:計算機中的流水線技術是把一個重複的過程分解爲若干個子過程,每個子過程與其他子過程並行進行。從本質上講,流水線技術是一種時間並行技術。通常我們描述的指令級並行性指的是在一個時鐘週期內能發射多條指令,但如果使用流水線技術,由於一個指令需要多個時鐘週期完成,因此仍然存在指令級並行的情況:每個時鐘週期都可以取得一個新指令,而前面的指令還在流水線中執行,例如一個簡單的五級流水線:
如果後續指令所需要的結果在此時已經可用,那麼流水線就可以流出一條指令。但對於部分存在數據相關或者分支跳轉的指令而言,下一條指令所需要的內容依賴於上一條指令的執行結果,此時就需要進行一定的調度或數據傳輸來避免流水線停頓。
-
多指令發送
:流水線技術雖然已經利用了一定的並行性來加速程序執行,但如果能通過配置多個可用的功能部件在每個週期發送多條指令,並行性還可繼續提升,即多指令發送技術,也稱多發射技術。常見的多發送機器有通過軟件管理其併發性的
VLIW
(Very Long Instruction Word,超長指令字) ,即通過一種非常長的指令組合,把許多條指令連在一起增加運算速度;或通過硬件管理的超標量
機器。簡單的硬件指令調度器根據指令獲取的順序執行指令,如果其碰到依賴先前指令的指令,需要等待依賴關係的解除(計算結果可用)才能進行下一步的計算。更加複雜的指令調度器可以通過動態地調整指令執行的序列來避免相關性造成的阻塞。
-
多核處理器
:近年來,由於摩爾定律的限制,僅僅提高單核芯片的速度會產生過多熱量且無法帶來相應的性能改善,因此引入了多核芯片來增加並行性。多核處理器是指在一枚處理器中集成兩個或多個完整的計算引擎(內核),此時處理器能支持系統總線上的多個處理器,由總線控制器提供所有總線控制信號和命令信號。多核處理器對應於線程級並行性。
代碼調度的相關約束
在討論代碼調度的相關算法之前,我們首先需要看一下代碼調度所需要遵守的一些基本約束條件。約束可以大致分爲三種類型:
控制依賴約束
:所有在源程序中執行的操作都必須在優化的程序中執行;數據依賴約束
:優化後的程序中的操作必須和源程序中的相應操作生成相同結果;資源約束
:特定機器上的資源是有限的,不能超額使用。
這些約束保證程序的優化可以正常進行,並生成和源程序相同的結果。但由於代碼調度改變了指令執行的順序,有可能優化後的程序在執行某一點上的內存狀態與優化前任何一點都不匹配。
我們來看看具體的一些依賴問題。
數據依賴
簡單來說,如果兩個操作訪問同一個變量,且這兩個操作中有一個爲寫操作,此時這兩個操作之間就存在數據依賴性,並且它們之間的相對執行順序必須保持不變。在代碼調度中可能出現的數據依賴有:
真依賴
:即寫之後再讀;反依賴
:讀之後再寫,如果調度時寫操作在讀操作前發生,就可能讀到錯誤的值。輸出依賴
:寫之後再寫,如果順序調換則會導致被寫位置上存放的是錯誤的值。
其中,後兩者被稱爲存儲相關的依賴
,可以通過在不同的內存位置存放不同的值來消除這些依賴關係。
內存訪問依賴
如果兩個不同的內存訪問指向同一個位置,就有可能存在內存訪問之間的依賴關係。內存訪問依賴關係比較複雜,尤其是對於非類型安全的語言(如C語言),要證明任意一對基於指針的內存訪問之間的獨立性需要負債的分析過程。主要的分析可以有以下幾種:
數組的數據依賴分析
:區分數組元素訪問中的下標值;指針別名分析
:如果兩個指針指向同一個對象,即互爲別名;過程間分析
:關於全局變量與參數之間的問題。
寄存器使用與並行性的折衷
在並行分析和調度中的機器無關中間表示所使用的無限多個僞寄存器必須被映射到目標機器上的有限寄存器;而把幾個僞寄存器映射到同一個物理寄存器會生成一定的存儲依賴,導致限制了指令級的並行性。從另一方面來說,並行性也產生了更多的存儲需求。因此,儘量降低寄存器使用數量的目標與最大化指令並行性的目標直接衝突。
寄存器分配階段與代碼調度階段的順序也會影響到並行性與存儲器數量,因此在某些時候可以採用層次化
的方式來處理,例如從最內層循環開始進行代碼優化,先進行指令調度,再進行寄存器分配,再對代碼進行調度;對外層循環依次重複此過程。
控制依賴
如果說指令A的結果決定了指令B是否執行,那麼就可以說指令B是控制依賴
於指令A的。一個優化後的程序必須執行源程序中所有的運算,也可以執行更多的指令來增加並行性。
投機執行
如果我們知道一條指令可能會執行,並且有空閒的資源來"免費"執行這個指令,就可以先投機地執行這個指令;如果這個投機是正確的,就能加速程序執行。如內存加載指令就能從中獲取較大好處,很多現代高性能處理器都有對其的支持功能,如:
- 預取指令
- 毒藥位
- 帶斷言的執行
下一篇:編譯過程中的並行性優化(二):基本塊與全局代碼調度算法
我的GIS/CS學習筆記:https://github.com/yunwei37/myClassNotes
<一個浙大GIS/CS小白的課程學習筆記 >