宋強,唐俊龍,陳照云,時洋,譚期軒,肖紫陽,鄒望輝
(1.長沙理工大學(xué)物理與電子科學(xué)學(xué)院,湖南 長沙 410114; 2.國防科技大學(xué)計算機學(xué)院,湖南 長沙 410073)
隨著人工智能、大數(shù)據(jù)、云服務(wù)等新興技術(shù)的發(fā)展,計算已經(jīng)成為科學(xué)研究與工業(yè)生產(chǎn)必不可少的工具與手段[1]。高性能計算在材料科學(xué)[2]、大數(shù)據(jù)[3-4]、地震模擬[5]、天文觀測、生物醫(yī)學(xué)等領(lǐng)域的大規(guī)模數(shù)據(jù)模擬與仿真中發(fā)揮著重要的作用,是推動國家科技發(fā)展與進步的有力手段之一。國防科技大學(xué)作為國內(nèi)最早開始高性能計算處理器自主設(shè)計的優(yōu)勢單位之一,研發(fā)了采用中央處理器(CPU)+通用數(shù)字信號處理器(GPDSP)片上異構(gòu)融合架構(gòu)的高性能加速器,其中GPDSP加速核采用超長指令集(VLIW)+單指令多數(shù)據(jù)流(SIMD)的向量化架構(gòu),具備高性能、低功耗的特點,是新一代超算系統(tǒng)核心計算芯片的有力競爭者之一。為了充分發(fā)揮GPDSP的硬件性能,提升用戶編程友好性,從編譯器層面實現(xiàn)對GPDSP的良好支持與并行性挖掘是可行的辦法之一,也是當下亟待解決的重要問題。
低級虛擬器(LLVM)是當前主流的編譯框架之一,統(tǒng)一的中間表達(IR)與高度模塊化的設(shè)計是LLVM所具備的獨特優(yōu)勢。高度模塊化的設(shè)計降低了開發(fā)者工作的難度,統(tǒng)一的IR加強了對高級編程語言的支持,豐富的工具集極大地降低了開發(fā)者的工作量[6]。然而現(xiàn)有LLVM編譯框架對國產(chǎn)高性能加速器兼容效果較差,無法充分發(fā)揮GPDSP加速核的硬件優(yōu)勢與結(jié)構(gòu)優(yōu)勢,主要體現(xiàn)在以下幾個方面:1)指令調(diào)度模塊是面向通用處理器設(shè)計的,對VLIW體系結(jié)構(gòu)的支持不夠完善,在靜態(tài)功能單元分配以及完整的指令打包與排布優(yōu)化上具備可提升的空間;2)密集的數(shù)據(jù)計算指令排布容易造成更多的寄存器溢出,極大程度上影響性能發(fā)揮,尤其是對于靜態(tài)指令排布的VLIW架構(gòu);3)不支持GPDSP特有的向量指令,增加了開發(fā)者的開發(fā)難度,無法充分發(fā)揮GPDSP結(jié)構(gòu)在數(shù)據(jù)級并行上的優(yōu)勢。
本文基于主流LLVM編譯框架,結(jié)合高性能加速器GPDSP的體系結(jié)構(gòu)特點,優(yōu)化功能單元分配沖突檢測機制,提出支持靜態(tài)功能單元分配與寄存器壓力感知[7]的指令調(diào)度策略,充分挖掘程序指令級并行性能,為基于LLVM編譯框架的VLIW指令調(diào)度研究與實現(xiàn)提供借鑒;在此基礎(chǔ)上,結(jié)合自主GPDSP指令集,為用戶提供一系列豐富且規(guī)整的向量指令接口,從而有效降低用戶并行開發(fā)的難度,并且為用戶程序在指令集層面提供更多數(shù)據(jù)并行優(yōu)化的可能;最后,結(jié)合高性能加速器當前研究熱點與發(fā)展趨勢,對未來編譯器優(yōu)化方向提出一些后續(xù)的思考和討論。
高性能加速器核GPDSP基于自研指令集設(shè)計,采用VLIW+SIMD的標向量融合架構(gòu),最大可支持11條指令發(fā)射以及16寬度的向量單元[8],其內(nèi)核部分的主要結(jié)構(gòu)如圖1所示。該加速器主要由包含標量處理單元(SPU)、標量存儲單元(SM)的標量部件與包含向量處理單元(VPU)、向量存儲單元(AM)的向量部件以及取指派發(fā)單元構(gòu)成。其中:取指派發(fā)單元可以同時為標量部件與向量部件派發(fā)指令;標量處理單元主要負責標量數(shù)據(jù)處理與程序流控制,同時負責控制向量部件的運行;向量處理單元負責高密度數(shù)據(jù)的并行計算任務(wù)。向量處理單元由16個同構(gòu)的向量處理引擎(VPE)構(gòu)成,VPE內(nèi)部集成3個同構(gòu)的浮點乘加器(VMAC),標向量數(shù)據(jù)通過標向量協(xié)同單元(SVR)進行數(shù)據(jù)共享,可以通過SVR共享寄存器使用廣播操作將一個標量寄存器的值傳遞到16個向量寄存器中[9]。GPDSP核內(nèi)包含存儲器直接訪問部件(DMA),支持包括點對點、廣播、二維索引傳輸?shù)褥`活高效的傳輸方式,使得GPDSP能夠適應(yīng)更多的應(yīng)用需求。
圖1 GPDSP結(jié)構(gòu)Fig.1 GPDSP structure
GPDSP架構(gòu)具有典型的VLIW特征,與傳統(tǒng)處理器相比,為了增加運算性能并且簡化加速器設(shè)計,GPDSP加速核不具備動態(tài)指令分配的特性,而是將包含一條或多條指令的完整的指令包分發(fā)給確定的功能單元,指令的功能單元分配、排布優(yōu)化與打包等任務(wù)通過編譯器的指令調(diào)度模塊完成,編譯器的指令調(diào)度優(yōu)化性能也直接影響GPDSP的硬件性能釋放率。同時為了提升數(shù)據(jù)并行處理能力,GPDSP融合了SIMD的架構(gòu)特征,在指令集設(shè)計上提供了豐富的向量指令,加速器內(nèi)部的向量處理單元為向量指令的實現(xiàn)提供了良好的硬件支持,編譯器的支持能夠讓用戶更好地使用GPDSP向量部件,有效降低了用戶的開發(fā)難度。
LLVM編譯架構(gòu)由編譯前端、代碼優(yōu)化器及編譯后端三部分組成[10],如圖2所示。其中,編譯前端負責將高級語言(如C、C++、Python等)編寫的程序轉(zhuǎn)化成LLVM特定的中間表示形式LLVM IR;代碼優(yōu)化器主要對程序進行動態(tài)、靜態(tài)分析及代碼優(yōu)化工作;編譯后端負責將優(yōu)化后的MI形式指令翻譯成目標機器(如x86、ARM、RISCV等)的匯編代碼。LLVM編譯框架中包含許多優(yōu)化遍[11],包含指令調(diào)度、寄存器分配等,優(yōu)化遍能夠極大程度地影響編譯器最終的性能。
圖2 LLVM編譯器架構(gòu)Fig.2 LLVM compiler architecture
然而,在開源LLVM編譯器框架支持的眾多處理器中,少有與GPDSP架構(gòu)類似的,指令調(diào)度等關(guān)鍵優(yōu)化遍也無法滿足GPDSP使用需求。由于GPDSP采用VLIW+SIMD的異構(gòu)架構(gòu),擁有大量的向量化資源,且用戶程序多為數(shù)據(jù)密集型程序,執(zhí)行效率極其依賴編譯優(yōu)化效果,因此有必要對GPDSP提供向量與指令調(diào)度支持。本文針對GPDSP芯片向量資源豐富、不包含動態(tài)調(diào)度硬件的特點,設(shè)計并優(yōu)化支持GPDSP架構(gòu)的指令調(diào)度策略,并提供向量支持。
指令調(diào)度是編譯器在IR層進行程序優(yōu)化的一種技術(shù),目的是通過調(diào)整指令順序提高指令層上的并行度,使得程序在處理器上能夠高效運行。現(xiàn)有LLVM指令調(diào)度策略是面向通用處理器CPU設(shè)計的,開源LLVM編譯框架中僅有高通針對VLIW架構(gòu)做出支持與優(yōu)化,但高通與GPDSP架構(gòu)相差較大,對GPDSP架構(gòu)后端支持僅有借鑒意義,因此,本文結(jié)合GPDSP架構(gòu)特點設(shè)計專用于GPDSP加速核的指令調(diào)度模塊。指令調(diào)度模塊設(shè)計包含寄存器壓力感知調(diào)度與靜態(tài)功能單元分配2個部分:寄存器壓力感知調(diào)度基于pre-RA-sched設(shè)計,主要通過靜態(tài)分析程序信息預(yù)測寄存器壓力值,從而指導(dǎo)指令調(diào)度,獲取更優(yōu)的調(diào)度效果;靜態(tài)功能單元分配基于post-RA-sched設(shè)計,負責將功能單元在編譯階段靜態(tài)分配給每一條指令,通過沖突檢測機制保證功能單元分配的正確性,從而保證程序正確執(zhí)行,此部分是指令并行執(zhí)行的基礎(chǔ)。
寄存器壓力感知調(diào)度主要是在MI-scheduler過程中,通過程序靜態(tài)分析的方式預(yù)測寄存器壓力,從而指導(dǎo)指令調(diào)度的進行。指令調(diào)度與寄存器分配是一對相互矛盾的NP困難問題,主流的優(yōu)先級調(diào)度方法往往會產(chǎn)生一定程度的寄存器溢出,而在有大量高密度數(shù)據(jù)并行計算的GPDSP高性能加速器上,這一矛盾表現(xiàn)得更為明顯。采用峰值寄存器壓力感知方法(PERP)[12],結(jié)合GPDSP結(jié)構(gòu)特點設(shè)定代價函數(shù),并通過蟻群優(yōu)化(ACO)[13]算法實現(xiàn)寄存器壓力感知調(diào)度,可以有效減少寄存器溢出數(shù)量,提升程序執(zhí)行性能。寄存器壓力感知調(diào)度流程如圖3所示。
圖3 寄存器壓力感知調(diào)度流程Fig.3 Register pressure-aware scheduling process
寄存器壓力感知調(diào)度主要由兩部分組成:一是基于PERP的寄存器壓力感知;二是基于ACO算法的指令調(diào)度。
2.1.1 寄存器壓力感知算法
在指令調(diào)度過程中,通過def-use鏈獲取每條指令的寄存器定義及使用信息;在每一次最優(yōu)節(jié)點選取之后,獲取該節(jié)點的寄存器使用信息,分別將def和use信息放入DefList、UseList中,通過對DefList、UseList 2個隊列的信息進行分析與處理,當CurrCycle值變化時計算當前周期的寄存器壓力ERP,CurrCycle值是SchedBoundary類下的一個unsigned類型變量,被用來表示調(diào)度周期,使用getCurrCycle()方法獲得。PERP則是每個基本塊內(nèi)所有周期中最大的寄存器壓力值(ERP)。以Topdown調(diào)度方式為例,具體算法描述如算法1所示。
算法1寄存器壓力感知算法
輸入最優(yōu)節(jié)點SU(SUnit類型),當前CurrCycle
輸出當前周期寄存器壓力值ERP
1.RPValue ComputeRP(SU,CurrCycle){
/*獲取當前SU對應(yīng)的SDNode*/
2.SDNode MIScheNode = SU->getNode();
/*獲取當前SUnit使用和定義的值的列表*/
3.UseList=MIScheNode->getOperand();
4.DefList=MIScheNode->getValue();
/*將DefList插入活躍值列表LiveVRegList中*/
5.LiveVRegList.insert(LiveVRegList.end(),DefList.begin(),DefList.end());
/*將UseList中后續(xù)無使用且是在當前基本塊定義的值從LiveVRegList中刪除*/
6.For each I in UseList
/*檢查I是否在當前基本塊定義*/
7.If (LiveVRegList.contains(I) )
8.If (CheckForRelease)/*檢查I是否仍有使用*/
9.LiveVRegList.remove(I);
10.End If;
11.End If;
12.End For;
13.If (checkCurrCycle ());
/*若CurrCycle增加則計算ERP*/
14.ERP=LiveVRegList.size()-NumberPhyRegs;
15.}
算法1實現(xiàn)了對每個周期的寄存器壓力感知,當前基本塊的寄存器壓力PERP則是每個周期寄存器壓力ERP的最大值。
2.1.2 指令調(diào)度算法
通過ACO算法,在空間中搜索出大量滿足依賴的調(diào)度方案,根據(jù)結(jié)合GPDSP架構(gòu)特點設(shè)定的代價函數(shù),每次迭代選出代價函數(shù)最小的調(diào)度方案保存,達到最大迭代次數(shù)后,輸出最優(yōu)調(diào)度方案。該算法由3層循環(huán)組成,最外層循環(huán)與中層循環(huán)是重復(fù)的迭代搜索過程,最外層循環(huán)每次迭代都會實時更新參數(shù)信息,最內(nèi)層循環(huán)是基本塊調(diào)度循環(huán),一次循環(huán)生成一種調(diào)度方案。以TopDown調(diào)度方式為例,指令調(diào)度算法描述如算法2所示。
算法2指令調(diào)度算法
輸入基本塊MBB
輸出近似最優(yōu)調(diào)度方案
1.Scheduler.ACOSchedule(NumRegionInstrs){
/*初始化初始信息素濃度、信息素濃度矩陣、信息素揮發(fā)速率等參數(shù)信息,初始化DAG,構(gòu)造原始調(diào)度隊列ACOTopRoots*/
2.For(i=0;i<149;++i)/*設(shè)定最大迭代次數(shù)為150*/
3.For(j:Ant)/*蟻群大小設(shè)定為50*/
4.TopRoots=ACOTopRoots;
/*拷貝原始調(diào)度隊列*/
5.While(true)/*無未調(diào)度SU,退出循環(huán)*/
/*若可用隊列available中無SU,返回空,退出當前調(diào)度;若可用隊列available中只有一個SU,返回該SU;否則按照可用調(diào)度路徑上信息素濃度大小,使用輪盤賭法選出一個SU進行調(diào)度*/
6.SUnit*SU = SchedImpl->pickNoedwithACO
/*將返回的SUnit保存*/
7.j->ScheduleList.push_back(SU);
/*更新調(diào)度隊列,從available中移除SU并釋放SU的所有后繼,更新available隊列與pending隊列*/
8.updateQueues(SU,IsTopNode)
/*計算當前周期寄存器壓力*/
9.CurrERP=ComputeRP(SU,CurrCycle)
10.j->ERPList.push_back(CurrERP);
11.End While;
12.End For;
/*遍歷所有調(diào)度方案,計算代價函數(shù),返回最優(yōu)調(diào)度方案BestScheduleList*/
13.BestScheduleList=setbest();
/*根據(jù)每個調(diào)度方案的代價函數(shù)值更新信息素濃度矩陣,代價函數(shù)值越低,信息素濃度增加越多;代價函數(shù)值越高,信息素濃度增加越少*/
14.updatepheromone()
15.End For;
/*根據(jù)最優(yōu)調(diào)度方案進行調(diào)度*/
16.For(SU:BestScheduleList)
17.ScheduleMI(SU,IsTopNode);
18.End For;
19.}
PERP感知方法通過對程序的靜態(tài)分析得到基本塊的最大寄存器壓力,GPDSP高性能加速器結(jié)構(gòu)特點與應(yīng)用場景決定了用戶程序通常包含高密度數(shù)據(jù)并行計算,大多數(shù)應(yīng)用程序在調(diào)度過程中存在多寄存器壓力峰值的情況,調(diào)度時僅僅考慮最大寄存器壓力在多數(shù)情況下不能得到良好的效果。因此,設(shè)定式(1)、式(2)所示代價函數(shù),整體考慮寄存器壓力分布與峰值寄存器壓力對程序帶來的影響。
(1)
(2)
其中:S表示調(diào)度長度;ω表示寄存器壓力權(quán)重,該值越大,表示單位寄存器壓力對程序性能的影響越大,通過調(diào)整ω的值可以使程序偏向最小化調(diào)度長度或最小化寄存器壓力;Pmax表示峰值寄存器壓力;Perp表示單位周期的寄存器壓力;φ表示峰值寄存器壓力在整體寄存器壓力中的占比,φ值越高,表示峰值寄存器壓力對程序性能的影響越大,φ值越低,表示整體寄存器壓力對程序性能的影響越大。
靜態(tài)功能單元分配由后端描述、沖突檢測、功能單元分配3個模塊組成。其中:沖突檢測模塊主要負責檢測是否有可用功能單元、指令是否可分配功能單元等;功能單元分配模塊負責將可用功能單元與指令匹配;后端描述模塊保存指令的指令執(zhí)行進程表與調(diào)度約束等調(diào)度信息。
2.2.1 后端描述
后端描述文件FTSchedule.td主要保存所有指令的調(diào)度信息以及與GPDSP加速器硬件相關(guān)的調(diào)度約束。與RISCV、ARM架構(gòu)相比,GPDSP需要明確指令在執(zhí)行過程中對功能單元與讀寫端口的占用信息。因此,重構(gòu)LLVM的調(diào)度信息與使用方式,增加對讀寫占用的區(qū)分,具體格式如下:
InstrItinData
其中:Instr表示指令名稱;FuncUnitA、FuncUnitAw、FuncUnitB、FuncUnitBw表示所需的功能單元,使用w后綴對讀寫端口的占用進行區(qū)分,默認沒有w后綴表示占用功能單元讀端口;CycleA、CycleC表示占用對應(yīng)功能單元的周期數(shù);CycleB表示當前周期執(zhí)行完后輸出指令執(zhí)行結(jié)果所需要的周期數(shù);Index表示在輸出指令之后讀取或?qū)懭朊總€操作數(shù)所需要的周期數(shù),默認IndexA為目標寄存器。
2.2.2 沖突檢測模塊
通過沖突檢測保證功能單元分配的正確性,每個Cycle都會對需要發(fā)射的每條指令SUnit執(zhí)行沖突檢測模塊。輸入為指令SUnit,輸出為無沖突nohazard或有沖突havehazard,檢測流程如圖4所示。每次執(zhí)行首先會從AvailableQueue隊列中拿出一條指令輸入,通過getInstrDesc方法從后端描述文件FTSchedule.td中獲得該指令的指令流水信息,從而獲得執(zhí)行該指令所需要的功能單元、讀寫端口以及占用的周期數(shù),若該指令是偽指令或沒有指令執(zhí)行進程表,則直接返回nohazard。遍歷所有功能單元,通過RequiredScoreboard得分板獲得當前未被使用且可被分配的功能單元并存儲到FreeUnits中。沖突檢測時優(yōu)先進行讀沖突檢測,不存在讀沖突時再進行寫沖突檢測。讀寫沖突須同時滿足條件返回nohazard,該功能單元可被分配。
圖4 沖突檢測流程Fig.4 Conflict detection process
2.2.3 功能單元分配模塊
該模塊的目標是使所有的指令在滿足依賴關(guān)系的情況下完成功能單元的分配。所有AvailableQueue隊列中滿足沖突檢測的指令會執(zhí)行功能單元分配并調(diào)度,并將指令從AvailableQueue隊列中移除。若指令不滿足沖突檢測,即對該指令不存在可供分配的功能單元,則將該指令放入PendingQueue隊列中,等待下一次調(diào)度。當AvailableQueue為空時,Cycle加1,重新更新所有隊列,開始下一次調(diào)度。
GPDSP高性能加速器為向量指令實現(xiàn)提供了良好的硬件基礎(chǔ),用戶可以使用向量指令為程序帶來可觀的性能加速,這需要從編譯器層面實現(xiàn)向量指令支持,為用戶提供向量指令接口。向量指令支持主要由向量指令實現(xiàn)、向量指令接口映射與匯編指令生成3個部分組成。其中:向量指令實現(xiàn)主要是面向硬件的向量指令集定義;向量指令接口映射主要負責將用戶使用的向量接口通過builtin接口映射為intrinsic接口;匯編指令生成負責將intrinsic接口映射為LLVM IR并匹配對應(yīng)的向量指令,該部分與標量指令采用相同的指令選擇算法,參考LLVM指令選擇匹配模板即可,無需特殊處理與設(shè)計。因此,向量指令支持重點將放在向量指令實現(xiàn)與向量指令接口映射這2個部分。
為實現(xiàn)向量指令支持,新增了如表1所示的硬件支持的8種數(shù)據(jù)類型,所有數(shù)據(jù)類型的數(shù)據(jù)寬度均為1 024 bit。
表1 新增向量數(shù)據(jù)類型 Table 1 New vector data types
根據(jù)GPDSP指令集在后端描述文件中定義向量指令,以下給出定義向量指令的具體示例:
class 1OP_2OP_vrrr
其中:1OP_2OP_vrrr表示向量指令模板,具體向量指令的實現(xiàn)需要繼承對應(yīng)的向量指令模板;funct10表示指令的操作碼,對應(yīng)指令集中指令的機器碼;InsnCycle表示指令調(diào)度模式;opcodestr表示指令的名稱;u表示指令所需的功能單元;FTInstrAVR表示指令類型,包含對應(yīng)的指令功能信息。
根據(jù)GPDSP指令集,定義如表2所示的向量訪存、向量運算等9種類型向量指令。
表2 新增向量指令 Table 2 New vector instructions
考慮到給用戶提供一套規(guī)整的向量指令使用接口,且與GNU編譯器集合(GCC)提供的向量接口保持高度統(tǒng)一,有效降低用戶開發(fā)難度,提高用戶編程友好性,采用如圖5所示的多級接口設(shè)計方案,基于GPDSP指令集,設(shè)計builtin接口與intrinsic接口,并將builtin接口封裝提供給用戶。
圖5 向量指令接口設(shè)計方案Fig.5 Design scheme of vector instruction interface
每個builtin接口都具有唯一確定的編號。通過封裝的形式,將builtin接口封裝為與GCC一致的向量指令接口提供給用戶使用。builtin接口與intrin接口使用編寫的映射規(guī)則進行映射,每一個builtin接口都通過統(tǒng)一的接口編號綁定到對應(yīng)的intrinsic接口。
映射規(guī)則的實現(xiàn)是通過在機器描述文件intrinsic.td中編寫每一個intrinsic接口的記錄,該記錄維護builtin接口到intrinsic接口的索引,由tablegen工具自動生成C++轉(zhuǎn)換程序,實現(xiàn)接口的映射。以向量加法為例,映射規(guī)則設(shè)計如下:
def int_vec_add_v:GCCBuiltin<"__builtin_vec_add">,Intrinsic<[llvm_lvs_int_ty],[llvm_lvs_int_ty,llvm_lvs_int_ty],[IntrNoMem,Commutative]>
其中:__builtin_vec_add為綁定的builtin接口;int_vec_add_v為對應(yīng)的intrinsic接口內(nèi)部名稱;llvm_lvs_int_ty表示該加法操作的2個操作數(shù)都必須為lvector signed int類型數(shù)據(jù)。
特別地,部分源操作數(shù)包含立即數(shù)的指令,該類型的指令在匹配指令接口時,會由于重載導(dǎo)致立即數(shù)類型轉(zhuǎn)換為變量,無法匹配對應(yīng)的指令接口。針對該類型指令,通過添加廣播操作,將立即數(shù)廣播到寄存器中,以匹配不含立即數(shù)類型的向量接口,實現(xiàn)相同的指令執(zhí)行效果。
本文提出的編譯器設(shè)計與優(yōu)化基于LLVM 12.0的版本進行代碼功能實現(xiàn),采用GPDSP高性能加速器[14]作為硬件測試平臺,開展LLVM編譯器功能測試與性能測試,重點包含指令調(diào)度與向量指令接口2個方面。
選用GCC testsuite與SPEC CPU 2017 2個測試集對改進后的指令調(diào)度模塊進行功能驗證與性能測試;基于GPDSP增加的向量指令接口,額外增加部分測試代碼進行向量指令功能驗證與性能測試。所有測試代碼均由改進后的LLVM/Clang 12.0.0編譯生成。
4.2.1 指令調(diào)度模塊功能測試與分析
為驗證設(shè)計編譯器的正確性,對GCC testsuite中的所有測試程序與SPEC CPU2017標準測試集中編程語言不包含F(xiàn)ortran的30個測試題進行測試。以-O0優(yōu)化級別下通過率為基準,GCC testsuite測試集在各優(yōu)化級別下的測試結(jié)果如表3所示,可見,編譯器在-O1優(yōu)化級別下通過率為98.31%,在-O2、-O3、-Os優(yōu)化級別下通過率為98.73%。編譯及執(zhí)行未通過測試用例的主要原因是部分inline以及優(yōu)化處理與GPDSP架構(gòu)不兼容,這些測試用例后續(xù)不再引入測試集進行結(jié)果展示。
表3 GCC testsuite測試結(jié)果 Table 3 Test results of GCC testsuite
SPEC CPU2017測試集的測試結(jié)果如表4所示,可見,標準測試集中整型與浮點型2類測試例共29道測試題均能正常執(zhí)行通過。
表4 SPEC CPU 2017測試結(jié)果 Table 4 Test results of SPEC CPU 2017
通過GCC testsuite與SPEC CPU 2017這2個測試集測試結(jié)果的對比分析可以得出,改進指令調(diào)度模塊后LLVM編譯器能夠正常生成正確的匯編程序且程序能夠正確執(zhí)行,指令調(diào)度模塊功能驗證通過。
4.2.2 向量指令接口功能測試與分析
基于GPDSP指令集,編寫部分包含向量指令接口的測試代碼,通過改進后LLVM編譯器編譯生成可執(zhí)行文件,并使用GPDSP高性能加速器運行,統(tǒng)計對應(yīng)向量指令匯編代碼生成情況與程序執(zhí)行情況,測試結(jié)果如表5所示,可見,所有向量指令接口都能生成對應(yīng)的匯編指令,指令匹配正確,程序執(zhí)行結(jié)果正確,向量指令接口功能驗證通過。
表5 向量指令接口功能測試結(jié)果 Table 5 Function test results of vector intruction interface
4.3.1 整體性能測試與分析
選用GCC testsuite、SPEC CPU 2017測試集對編譯器整體性能進行測試,明確性能邊界。不同優(yōu)化選項下采用的編譯優(yōu)化手段各不相同:-O0優(yōu)化選項下不執(zhí)行任何編譯優(yōu)化,程序順序執(zhí)行;-O1優(yōu)化選項下會執(zhí)行一些不增加大量編譯時間的簡單優(yōu)化,可能會導(dǎo)致指令并行化;-O2優(yōu)化選項下會執(zhí)行絕大部分優(yōu)化,最大程度地挖掘指令并行性能。在實現(xiàn)支持靜態(tài)功能單元分配的指令調(diào)度后,為GPDSP執(zhí)行并行指令提供了軟件基礎(chǔ),保證了GPDSP指令并行執(zhí)行的正確性。
通過對比GCC testsuite測試集在-O0、-O1、-O2優(yōu)化級別下的執(zhí)行速度,對編譯器的整體性能進行評估,測試結(jié)果如圖6所示。以-O0優(yōu)化級別為基準,-O1與-O2優(yōu)化級別均能得到較少的程序執(zhí)行時間,其中:-O1優(yōu)化級別下平均加速比為3.082,最高加速比為38.174;-O2優(yōu)化級別下平均加速比為4.539,最高加速比為44.644。
圖6 GCC testsuite整體性能測試結(jié)果Fig.6 Overall performance test results of GCC testsuite
圖7所示為SPEC CPU 2017測試集測試結(jié)果,通過對比SPEC CPU 2017測試集在-O0、-O2優(yōu)化級別下的執(zhí)行速度,評估編譯器的整體性能。由圖可見:以-O0優(yōu)化級別為基準,浮點測試平均性能加速比為4.49,最高加速比為14.03;整型測試平均性能加速比為3.24,最高加速比為5.94。
圖7 SPEC CPU 2017整體性能測試結(jié)果Fig.7 Overall performance test results of SPEC CPU 2017
4.3.2 寄存器壓力感知性能測試與分析
基于寄存器壓力感知的指令調(diào)度方法能夠在程序編譯時靜態(tài)分析寄存器壓力值,通過代價函數(shù)選擇最優(yōu)調(diào)度方案,有效減少寄存器溢出數(shù)量,提升程序性能。寄存器壓力感知調(diào)度對數(shù)據(jù)密集型程序更為敏感,因此,采用SPEC CPU 2017測試工具進行性能測試,對比LLVM指令調(diào)度器默認調(diào)度方法與寄存器壓力感知調(diào)度方法的加速性能,編譯優(yōu)化選擇-O2-mllvm-enbale-regpressure-mllvm-debug-only=machine-scheduler,ref規(guī)模,測試結(jié)果如圖8所示??梢钥闯?使用寄存器壓力感知調(diào)度后程序可以得到明顯的性能改善,最高加速比為1.06,浮點測試平均加速比為1.024,整型測試平均加速比為1.016。
圖8 寄存器壓力感知性能測試結(jié)果Fig.8 Performance test results of register pressure-aware tests
4.3.3 向量指令接口性能測試
使用向量指令接口進行編程,通過編譯器生成對應(yīng)向量匯編指令,能夠極大地減少代碼體積,提升程序執(zhí)行效率。本文基于GPDSP向量指令集,增加了部分函數(shù)級測試代碼,對比使用向量指令前后的性能與代碼體積差異,結(jié)果如表6與圖9所示。從表6中可以看出,在使用向量指令后,程序執(zhí)行包數(shù)量平均減少95.41%,vsip_vcmagsq_f指令程序執(zhí)行包數(shù)量減少率最高,減少率為98.27%。從圖9中可以看出,使用向量指令后,所有程序都獲得了不同程度的性能提升,平均性能提升率為97.1%,最高性能提升率為98.7%。
表6 向量程序體積測試結(jié)果 Table 6 Vector program volume test results
圖9 向量程序運行時間測試結(jié)果Fig.9 Test results of vector program runtime
編譯器是用戶程序開發(fā)最重要的工具之一,能夠為用戶提供多種優(yōu)化手段,如何利用編譯器為用戶降低程序開發(fā)難度并且?guī)砀玫膬?yōu)化效果,始終是編譯器領(lǐng)域研究者所關(guān)注的問題。
在高性能計算領(lǐng)域,用戶程序大多具有數(shù)據(jù)密集型與計算密集型的特點,而用戶使用高度抽象的高級編程語言開發(fā)程序時無法從細粒度的指令集層面考慮熱點代碼片段的數(shù)據(jù)訪存開銷與硬件資源利用率,因此,本文結(jié)合當前高性能計算領(lǐng)域研究熱點,從編譯器的角度出發(fā),對未來優(yōu)化方向進行討論和探索:
1)組合優(yōu)化[15-16]?,F(xiàn)有編譯器框架的優(yōu)化方式大多是分開進行的,問題在于多種優(yōu)化方式之間可能存在隱形的影響,例如指令調(diào)度與寄存器分配。指令調(diào)度以最大化指令級并行(ILP)為目標,在寄存器分配階段時可能導(dǎo)致寄存器溢出,需要將多余的變量值寫回內(nèi)存中,從而產(chǎn)生額外的訪存開銷,影響程序性能。解決這類問題的主要思路是通過組合優(yōu)化的方式,將多種優(yōu)化方式組合在一個優(yōu)化中,從全局的角度進行優(yōu)化,但這會導(dǎo)致極大的時間開銷。目前組合優(yōu)化的效果并不理想,需要探尋新的優(yōu)化方式與優(yōu)化組合,以達到更好的性能提升與更低的時間開銷。
2)自動向量化[17-18]。相比于手動編寫SIMD向量程序,自動向量化能讓程序員繼續(xù)采用串行思想進行編程,而不用考慮SIMD擴展部件的功能與特點,減少程序員編程難度與工作量。目前,自動向量化技術(shù)還存在許多不足之處,值得進一步研究,例如馮竟舸等[19]提出的引入動態(tài)規(guī)劃、整數(shù)線性規(guī)劃等組合優(yōu)化方法進行并行度的選擇、引入同步多線程(SMT)等理論方法解決向量化分組問題、對SIMD擴展部件進行特異化設(shè)計等方向都是未來可繼續(xù)研究的方向。
3)自動調(diào)優(yōu)[20-22]。GCC和LLVM中都包含有大量的優(yōu)化方式,通過-O參數(shù)設(shè)置選擇對應(yīng)的優(yōu)化級別是當下普遍的選擇[23-24],然而固定組合優(yōu)化方式并不能總是帶來更好的程序執(zhí)行性能,不同參數(shù)因子、優(yōu)化選項、優(yōu)化選項執(zhí)行順序?qū)Τ绦虻男阅芏紩聿煌挠绊?。通過迭代編譯或機器學(xué)習的方式在大量訓(xùn)練下進行自適應(yīng)的因子選擇、自動優(yōu)化編譯選項、內(nèi)聯(lián)決策和分支概率預(yù)測等能為編譯器帶來更多性能提升的空間[25]。
本文結(jié)合高性能加速器與其加速核GPDSP的結(jié)構(gòu)特點,提出了支持功能單元靜態(tài)分配與寄存器壓力感知的指令調(diào)度策略,為高性能加速器的指令并行執(zhí)行提供軟件支持,減少數(shù)據(jù)密集型應(yīng)用普遍存在的寄存器溢出,同時使用向量intrinsic方法對GPDSP向量指令集提供了支持。實驗結(jié)果表明:優(yōu)化后指令調(diào)度在功能上能夠?qū)崿F(xiàn)對高性能加速器的良好支撐,相對于LLVM -O0優(yōu)化方法,使用GCC testsuite測試在-O2優(yōu)化選項下能夠?qū)崿F(xiàn)平均4.539的加速比,使用SPEC CPU 2017浮點測試與整型測試在-O2優(yōu)化選項下分別實現(xiàn)了平均4.49、3.24的加速比;使用寄存器壓力感知的指令調(diào)度在浮點程序與整型程序上分別實現(xiàn)了平均1.024、1.016的性能加速比;相對于標量程序,使用向量指令接口實現(xiàn)的向量程序在執(zhí)行包數(shù)量上平均減少95.41%,平均性能加速比為35.2,降低了用戶編程難度,提高了用戶友好性。未來將繼續(xù)優(yōu)化寄存器壓力感知調(diào)度方法,探索新的寄存器壓力感知策略,并進一步支持向量程序指令調(diào)度,提升向量部件資源利用率。同時,將持續(xù)研究編譯器層面理論方法,從軟件角度挖掘高性能計算的優(yōu)化空間。