曹建立, 仲怡寧, 趙晨陽
(洛陽師范學院 數(shù)學科學學院, 河南 洛陽 471934)
GPGPU-Sim是一款用CPU來模擬GPU運行過程的模擬器軟件[1]. 該模擬器的運行統(tǒng)計結果可以達到時鐘周期的精度, 其代碼和配置文件可根據(jù)用戶需求來修改, 并且提供了可視化工具Aerial Vision[2]來幫助用戶直觀地觀察Kerne內(nèi)核的運行狀態(tài), 此外, 還集成了可以提供功耗報告的功耗分析模塊GPUWattch[3]. 因此, GPGPU-Sim成為大量學者研究GPU體系結構、驗證GPU新設計方案和調度算法的工具. 如Sahoo等[4]利用GPGPU-Sim來研究GPU緩存管理方案, Li等[5]利用GPGPU-Sim來研究Bypass技術在GPU上的應用效果, Mu等[6]利用GPGPU-Sim來研究GPU中的內(nèi)存調度優(yōu)化.
該模擬器基于Ubuntu10.10開發(fā), 在Ubuntu 10.10版本和12.04版本上運行良好,但在16.04及之后的版本上運行時, 會出現(xiàn)動態(tài)鏈接庫不兼容的問題. Ubuntu 10.10版本和12.04版本的操作系統(tǒng)分別于2010年10月和2012年4月發(fā)布, 目前看來已經(jīng)比較陳舊, 大量主流的計算機硬件已經(jīng)不再支持該版本的安裝與運行. 于是, 對于眾多通過GPGPU-Sim來研究GPU體系結構的學者來說, 繼續(xù)使用該軟件存在兩個問題: 一方面, 目前主流的計算機硬件具備較高的運算能力, 但是因為不支持Ubuntu12.04之前的版本, 所以無法直接運行GPGPU-Sim; 另一方面, 能運行老版本Ubuntu系統(tǒng)的硬件大多比較陳舊, 且運行GPGPU-Sim時效率十分低下. 雖然這些問題可以通過在新硬件上運行虛擬機來解決, 但是經(jīng)過實驗對比, 虛擬機的運行效率很低, 性能大約是直接在硬件上運行時的2/3. 因此, 研究讓GPGPU-Sim可以在新的軟硬件平臺上直接運行的方法, 對于廣大模擬器使用者和研究者有著現(xiàn)實意義.
GPGPU-Sim小組在說明文件中指出了開發(fā)GPGPU-Sim的目的: 一方面可以讓研究者分析CUDA程序瓶頸或者分析調度算法的效果; 另一方面可以讓沒有安裝Nvidia硬件的計算機也可以運行CUDA程序. 因此GPGPU-Sim提供了性能模擬和功能模擬兩個模式: 功能模擬僅輸出和真實硬件相同的運行結果; 性能模擬則在輸出結果的同時, 還輸出大量GPU內(nèi)部寄存器、緩存、通信網(wǎng)絡和功耗等信息.
GPGPU-Sim開發(fā)小組在內(nèi)存中利用堆棧、隊列、數(shù)組等數(shù)據(jù)結構模擬了GPU硬件中的流水線、緩存和寄存器等部件, 并利用CPU來模擬GPU中Kernel程序的數(shù)據(jù)流動和指令執(zhí)行的過程. GPGPU-Sim編譯后會產(chǎn)生libcudart.so和libOpenCL.so兩個動態(tài)鏈接庫. 之后使用這兩個庫替代系統(tǒng)中的默認庫, 則應該由GPU硬件來執(zhí)行的PTX指令會被攔截, 轉由CPU來模擬執(zhí)行[7]. Kernel程序的硬件執(zhí)行方式和軟件模擬調用關系如圖1所示.
圖1 Kernel程序的硬件執(zhí)行方式和軟件模擬調用關系
對于安裝有Nvidia硬件的平臺, 在運行時也可以選擇使用真實硬件運行. 切換方式是修改名為LD_LIBRARY_PATH的系統(tǒng)變量, 根據(jù)要求指向驅動程序的動態(tài)鏈接庫, 或者GPGPU-Sim產(chǎn)生的動態(tài)鏈接庫.
研究者通過修改GPGPU-Sim的源代碼來實現(xiàn)新的資源調度算法、新的緩存置換策略和新的設計的過程中, 往往需要用gdb來追蹤、調試代碼. 這就要求虛擬機調用的動態(tài)鏈接庫以源碼編譯的方式得到, 且調試時能訪問到源代碼.
GPGPU-Sim的編譯過程已經(jīng)有大量的文獻可以借鑒, 在此不再贅述. 在Ubuntu 12.04之前的版本上, 經(jīng)過編譯與環(huán)境變量配置, 即可順利用模擬器來替代GPU硬件, 以實現(xiàn)CUDA程序的模擬.
但是在Ubuntu 16.04版本以及之后的版本上, 按照傳統(tǒng)的方法安裝配置后, 啟動基準程序可能導致運行錯誤.
此時可以使用gdb調試工具加載程序錯誤產(chǎn)生的core文件, 并用bt命令查看調用堆棧, 會顯示大量同C++動態(tài)鏈接庫相關的錯誤報告信息.
究其原因, 是因為GPGPU-Sim基于GCC 4.X.X版本開發(fā), 而較新版本的Ubuntu系統(tǒng)使用了高版本的GCC和GC++編譯器(5.X.X), 不同版本的CC++的動態(tài)運行庫文件之間存在不兼容的問題, 從而導致了調用運行庫出錯. 其核心問題是GPGPU-Sim要求CC++動態(tài)鏈接庫版本不能超過6.0.14.
解決該問題的常用辦法是通過編譯低版本GC++編譯器源碼, 或者用apt-get install命令通過參數(shù)指定低版本的方式來重裝GCC和GC++編譯器, 然后重新編譯GPGPU-Sim. 該方法的缺點是操作比較復雜和耗時. 更重要的是, 將系統(tǒng)的GCC和GC++編譯器強制降低版本后, 可能給其他軟件的編譯和運行帶來諸多問題.
經(jīng)過反復比較和實驗, 筆者發(fā)現(xiàn)了一種便捷、不干擾其他程序運行的解決辦法: 在運行GPGPU-Sim前, 用低版本GC++的庫文件替代當前系統(tǒng)的庫文件; 待運行結束后, 再將庫文件替換回來. 替換操作無需反復復制和刪除文件, 可以通過靈活的鏈接命令來實現(xiàn).
例如, 本文實驗平臺中, 使用locate命令查找, 發(fā)現(xiàn)GPGPU-Sim需要的GC++動態(tài)鏈接庫文件libstdcC++.so.6位于/usr/lib/x86_64-linux-gnu/目錄下, 該文件其實是一個鏈接文件, 指向同目錄下的庫文件libstdcC++.so.6.0.21. 替換操作步驟如下.
(1)將老版本的動態(tài)鏈接庫文件libstdcC++.so.6.0.14復制備份到庫目錄下, 該版本的鏈接庫文件可以從GPGPU-Sim官網(wǎng)提供的虛擬機中提取.
(2)運行GPGPU-Sim前, 先將系統(tǒng)默認的鏈接文件libstdcC++.so.6刪除, 然后重建該鏈接文件并指向低版本的動態(tài)鏈接庫文件libstdcC++.so.6.0.14. 可以使用下面命令:
cd /usr/lib/x86_64-linux-gnu/
rm libstdcC++.so.6
ln -s libstdcC++.so.6.14 libstdcC++.so.6
(3)此時可以順利運行GPGPU-Sim, 采集和分析GPU模擬數(shù)據(jù).
(4)運行完成后, 需要把動態(tài)庫修改回來, 否則可能造成其他程序運行不正常甚至操作系統(tǒng)不正常. 可以使用下面命令:
cd /usr/lib/x86_64-linux-gnu/
rm libstdcC++.so.6
ln-s libstdcC++.so.6.21 libstdcC++.so.6
如果忘記了將鏈接指向高版本的動態(tài)庫, 并且進行了關機或者重啟操作, 可能導致系統(tǒng)再次啟動時發(fā)生異常, 并無法進入圖形界面. 此時需要按下Ctrl+Alt+F1(F1到F6的文本模式均可, F7是正常啟動的圖形界面)切換到文本模式終端, 然后執(zhí)行上面第(4)步的修改鏈接操作, 重新啟動即可.
筆者還建議將第(2)步和第(4)步的命令寫入shell腳本, 賦予執(zhí)行權限, 并將第(2)步腳本加入系統(tǒng)啟動服務, 在第(4)步加入系統(tǒng)關閉、重啟服務, 讓這些操作自動執(zhí)行, 以避免忘記. 待對GPGPU-Sim的研究完成后, 再移除這些腳本.
通過對GPGPU-Sim編譯過程和運行過程的分析, 發(fā)現(xiàn)編譯源代碼的目標是獲得libcudart.so、libOpenCL.so兩個動態(tài)鏈接庫. 因此, 對于只需要運行模擬器獲得輸出結果, 而無需在運行期間追蹤模擬器代碼的應用場景, 可以省略下載、編譯源碼這一步, 即直接將編譯好的動態(tài)鏈接庫復制到本機, 并經(jīng)過適當?shù)沫h(huán)境配置(CUDA路徑、so動態(tài)鏈接庫路徑和環(huán)境變量等)來運行模擬器. 當然, 模擬器運行所需要的其他依賴庫也要提前安裝.
具體步驟如下:
(1)復制已經(jīng)編譯好的libcudart.so、libOpenCL.so庫文件目錄到本計算機.
(2)配置LD_LIBRARY_PATH環(huán)境變量, 指向復制來的動態(tài)鏈接庫文件, 替代系統(tǒng)中原有的庫文件(在安裝GPU硬件和驅動程序的情況下).
(3)復制GPGPU-Sim配置文件: gpgpusim.config、gpuwattch_gtx480.xml、config_fermi_islip.icnt到CUDA程序同一目錄下. 該步驟建議使用鏈接文件完成, 避免冗余文件.
(4)啟動CUDA程序.
該方法適合使用多臺計算機來并行模擬基準測試程序的研究者, 可以有效避免在多臺計算機上傳輸代碼和編譯代碼的重復工作.
GPGPU-Sim利用軟件來模擬硬件執(zhí)行過程, 大約有10萬左右的減速因子, 每個benchmark過程需要執(zhí)行時間少則幾十分鐘, 多則幾天. 如果采用串行的方式來執(zhí)行幾十個benchmark, 則每輪測試會消耗數(shù)周時間, 這對使用者來說是非常高的時間成本. 目前無論服務器、臺式機還是筆記本電腦, 大都配備了多核處理器, 合理利用多核并行, 能大大加速測試過程. 并行執(zhí)行的關鍵是使用后臺執(zhí)行操作符nohup &, 并使用awk和grep工具將輸出數(shù)據(jù)中的重要數(shù)據(jù)篩選出來, 存入指定的文件中, 以備分析. 以最常使用的ISPASS-2009基準程序集為例, 其腳本內(nèi)容如下: 讀取benlist文件中保存的需要運行的基準程序所在的文件夾名字, 然后進入文件夾, 并用后臺方式執(zhí)行README.GPGPU-Sim腳本, 以創(chuàng)建一個執(zhí)行腳本時指定的名字的文件, 之后將輸出結果中關于L1緩存、GPU運行周期數(shù)、Kernel名字的信息保存到該文件中. 其他的基準程序集也可用該技術來并行執(zhí)行. 其主要代碼如下:
#! /bin/bash
if [ -z MYM1 ]
then
echo "input log file name."
else
curdir=′pwd′
num=′wc -l benlist|awk ′{print MYM1}
for (( benno=1; benno <= MYMnum ; bennoC++))
do
xxx=′sed -n "MYM{benno} p" benlist′
echo "MYMbenno == MYM{xxx}"
if [ -d MYM{xxx} ]
then
cd MYMxxx
nohup ./README.GPGPU-Sim | egrep "g_fetch_access_L1I | gpu_sim_cycle| kernel_name">>MYM1 &
end=MYM(date +%s)
cd MYMcurdir
else
echo -e "MYM{benno} MYM{xxx} not run."
fi
done
fi
待模擬運行完成后, 可以編寫腳本, 將不同benchmark的多個指標提取出來, 以表格的方式顯示, 方便對比. 其主要代碼如下:
#! /bin/bash
fsource1=MYM1
outfile=MYMfsource1.get
echo -e "name pre_mem tot_cycle tot_insn" >>MYMoutfile
num=′wc -l benlist|awk′{print MYM1}
for (( i=1; i <= MYMnum ; iC++))
do
xxx=′sed -n "MYM{i} p" benlist′
if [ -d MYM{xxx} ]
then
tail -n 19 MYM{xxx}/MYMfsource1 |grep "gpu_tot_sim_cycle" |awk ′{printf("%d ", MYM6) }′ >>MYMoutfile
tail -n 19 MYM{xxx}/MYMfsource1 |grep "gpu_tot_sim_insn" |awk ′{printf("%d ", MYM6) }′ >>MYMoutfile
tail -n 19 MYM{xxx}/MYMfsource1 lgrep "gpu_tot_ipc" |awk ′{printf("%f ", MYM6) }′ >>MYMoutfile
fi
done
研究者有時需要研究不同參數(shù)配置下的性能指標, 需要進行多輪的修改配置文件、運行benchmark過程. 此時需要提前修改好多個配置文件, 以不同名字命名, 然后在每輪測試開始前將鏈接指向不同的配置文件, 并利用sleep命令等待最長的benchamark完成. 這樣可以自動化完成多種配置下整個基準程序集的模擬. 其主要代碼如下:
run_dir=/home/sim/ispass2009-benchmarks #指向基準程序所在目錄
config_dir=/home/sim/configs/GTX480 #指向GPUSim配置文件所在目錄
for n in 4 8 16
do
cd MYMconfig_dir
rm gpgpusim.config
ln -s gpgpusim.config.ori.MYM{n}kL1I gpgpusim.config #指向不同配置文件
cd MYMrun_dir
./runall.sh out_MYM{n}kL1I_lrr.txt#調用腳本后臺運行全部測試程序
sleep 2h # 等待本輪全部測試完成
done
GPGPU-Sim運行過程中會先使用CUDA SDK的cuobjdump工具來分析CUDA程序, 從中提取出GPU Kernel的PTX和SASS代碼, 并寫入中間文件, 然后讀取這些指令進行模擬. 在默認配置下, 這些文件會保留下來. 研究這些中間文件的內(nèi)容, 對理解GPGPU-Sim的工作原理, 以及程序的運行狀況有很大幫助. 例如, Liang等[8]基于PTX指令集的load.global指令展開研究, 設計了緩存Bypass編譯器框架. Cao等[9]利用PTX指令文件分析了CUDA程序中Kernel容量的分布情況, 并設計了指令緩存預取機制.
log文件中記錄了GPU各部件的功耗狀況, 可用于GPU功耗研究. 中間文件和log文件含義如表1所示.
表1 中間文件和log文件含義
本文分析了在高版本Ubuntu系統(tǒng)上運行GPGPU-Sim、調用動態(tài)庫時發(fā)生的兼容性問題, 并提出了解決方案, 之后介紹了一種無需編譯源代碼即可運行GPGPU-Sim的簡單方法. 以ISPASS-2009為例, 給出了可以自動運行測試、采集結果的腳本參考文件. 該方法可以極大提高工作效率.