目錄
一、實驗環境
工具清單 |
|
虛擬機 |
VMware Workstation 14.1.1 build-7528167 |
系統鏡像 |
ubuntu-16.04.3-desktop-amd64.iso |
內核版本 |
Linux ubuntu 4.13.0-41-generic |
gcc版本 |
version 5.4.0 20160609 |
處理器內核總數 |
4 |
運行內存 |
4GB |
二、專題一之積分計算圓周率
2.1向量優化
根據求pi公式,嘗試進行編程實踐,分別給出串行、sse-float、sse-double、avx-float、avx-double、循環展開六個版本的代碼(result.c),觀察實驗結果:
由於計算pi時計算結果的精度和耗費時間與dt大小的選取密切相關,在該專題實驗中統一取dt=1280000,最終發現符合實驗指導書中所給規律:
AVX-float>AVX-double≈SSE-float>SSE-double>serial
在規定的五個版本基礎之上,做了一次簡單的循環展開測試,優化結果也比較明顯。由於通過手工進行循環展開,進而爲每一次循環體中更多指令的流水並行提供了可能,但由於指令系統和編譯器本身的侷限性,不可能完全依靠手工循環展開進行優化,最佳的方案需要取一個折中。
- 問題一:編譯選項問題
最開始代碼未整合,各個版本分別放在一個c文件中,這樣在編譯時加的優化編譯選項有所區別,如對串行版本,僅添加-O3,對sse版本添加-O3和-msse2,對avx版本添加-O3和-mavx2,分別編譯運行,發現結果非常不好。另外,在對串行版本代碼的編譯過程中發現僅添加-O3和添加-O3 -msse2 -mavx2的結果也大不相同,明顯後者較於前者做了更多“潛”優化,這樣的話我們得到的結果從試驗方法上來講就不科學。
考慮到機器每次運行時的環境狀態差異,以及編譯選項不統一引來的“潛優化”問題,最終選擇統一到一個c文件中,並對整個文件添加統一的優化選項,並進行多次試驗求其平均值,發現試驗結果良好,比較有說服力,驗證了向量優化的實用性。
編譯命令:gcc -mavx2 -msse2 -fopenmp -O3 pi.c -o result
- 問題二:精度問題
double和float型數據在機器中佔的字節數是不同的,複習計組過程中重新學習了浮點數float和double型數據在機器中的存儲方式。根據計算公式理論上來講,dt選擇越大,試驗結果越逼近pi的真實值;然而當dt較大時會發現float版本的試驗結果與真實pi的值相差甚遠,可以認爲已經從“誤差”級別轉爲“錯誤”級別。因此我們需要找到一個dt的折中值,最終選擇dt=1280000,試驗結果比較好。精度的問題在大型科學計算中顯得異常重要,啓示我們在未來追求速度和精度的計算之道中也要更加理性的選擇,妥善考慮。
2.2 OpenMP優化
OpenMP使用FORK-JOIN並行執行模型。所有的OpenMP程序開始於一個單獨的主線程。主線程會一直串行地執行,一旦遇到並行域會根據機器情況和編譯配置屬性實現多線程並行,特別適和對與該專題中類似計算密集型任務的優化。
給出openmp和openmp+simd兩種版本的代碼,觀察實驗結果,符合預期:
實驗中dt依然取1280000;
- 問題一:openmp任務兩級並行(手工向量化)
對計算任務進行openmp優化時,首先考慮任務的劃分問題,由於實驗環境中處理器核心數爲4,對dt級別的計算任務分解爲4個dt/4級別的任務,然後針對dt/4大小的任務考慮進行simd的進一步優化;
圖中二層循環處比較關鍵,由於已經給每個線程劃分一定的計算任務量,所以需要設計一個統一的計算任務體針對於每一個線程能如期執行。最開始由於考慮不周,將j的初始值定爲0,這就導致每個線程分的的計算任務量是不同的,最終pi的累加值遠大於真實值;
在尋找問題原因時採用打印變量值進行一步步的摸索,最終將問題定位於二層循環處;最終,成功實現openmp+simd的兩級並行優化,進一步增加優化程度。
三、專題二之測試SPECOMP2012
3.1初步瞭解SPECOMP
SPEC是一家非營利性公司,旨在建立,維護和認可標準化基準和工具,以評估最新一代計算系統的性能和能效,其測試範圍覆蓋雲、cpu、圖形和工作站、web服務器等,在本次測試中我們使用的SPECOMP爲高性能計算測試對象中的一個分支-OPENMP。
SPECOMP2012基準被設計用於測量使用基於OpenMP的3.1標準共享存儲器的並行處理應用的性能。該基準還包括一個可選的指標,其中包括功率測量,共包括14個科學和工程應用代碼,涵蓋從計算流體動力學(CFD)到分子建模到圖像處理的所有內容。可選的能耗測量基於SPEC功率和性能基準測試方法,該方法提供了有關如何將功率度量標準集成到標準化基準測試中的詳細信息。
3.2系統基本配置
|
|
hw_cpu_name |
Intel Core i5-7300HQ |
hw_disk |
36 GB |
hw_memory001 |
3.830 GB |
hw_nchips |
4 |
sw_file |
ext4 |
sw_os001 |
Ubuntu 16.04.3 LTS |
sw_os002 |
4.15.0-36-generic |
sw_state |
Run level 5 |
3.3實踐
安裝過程整體順利,在真正開始試驗之間需要仔細閱讀文檔,另外可以登陸SPEC官網查詢更加詳細的使用指導[1]。
測試過程中設置運行選項爲:
runspec --config=gcc.cfg --size=test --tune=base --iterations=1 --size=test --threads=X –I all
接下來會先進行編譯工作,隨後根據線程設置數運行。
3.3.1 測定不同線程數的影響
357和363在執行過程中報錯,結果日誌中的數據不準確,原因是虛擬機中的內存大小不符合運行所需標準。
- export OMP_NUM_THREADS=1
- export OMP_NUM_THREADS=2
- export OMP_NUM_THREADS=4
- export OMP_NUM_THREADS=7
- export OMP_NUM_THREADS=10
關於不同線程數情況下的結果,發現有三個耗時較長的程序,序號分別爲350、360、371,隨着環境變量中默認線程數的變化,發現對於350程序,線程數設置從1到4時,運算時間逐漸減少,當線程數設置超過4以後,運行總時間穩定在125s左右,這與omp並行程序一般的運行規律存在衝突;對於360、371程序,明顯可以看到當線程數設置爲4時,並行加速效果最佳,當超過4以後,由於機器本身的侷限性以及線程數增加引起的開銷增加,加速效果下降,符合常理,其中特別是371程序在線程數超過4以後運算時間明顯增加,猜測代碼中應該有大量的omp並行計算任務,觀察源文件夾中這幾個文件,與猜想一致。
帶着疑問,從代碼中嘗試發現問題,對於350程序,發現主要的計算任務中用了很多的MPI函數,而omp程序只佔了一小部分,所以猜測程序總體上運行時間受MPI進程數和omp線程數的綜合影響。
3.3.2測定不同調度方式的影響
- export OMP_SCHEDULE=dynamic
- export OMP_SCHEDULE=static
- export OMP_SCHEDULE=guided
三種調度方式中可以看出指導性和靜態方式對360程序的加速效果較明顯。
通過本次試驗,基本熟悉了SPECOMP測試的流程方法,初步驗證了omp的並行加速效果以及與機器配置的密切相關性。但仍存在一些問題未解決,如研究357和363程序的內存最低需求,探究350程序在線程數大於4後,加速效果未出現降低,不符合omp程序運行的一般經驗。另外,相比以前使用的Linpack性能測試,結果明顯不如Linpack來的直觀,但後者所需的配置文件也較複雜一點。
四、專題三之矩陣乘法優化
在數學中,矩陣(Matrix)是指縱橫排列的二維數據表格,最早來自於方程組的係數及常數所構成的方陣,這一概念由19世紀英國數學家凱利首先提出,現如今已成爲高等代數學中的常見工具,並廣泛用於統計分析等應用數學學科中,陣乘法同時也是並行計算領域常常被用來作爲範例的一個話題。它的特點是首先計算量可能相當大,適合利用並行實現來提高效率。其次,它所使用的各種數據之間沒有相互依賴性,可以充分使用並行處理的計算資源。
4.1普通版本
矩陣相乘最重要的方法是一般矩陣乘積。它只有在第一個矩陣的欄數(column)和第二個矩陣的列數(row)相同時纔有定義。一般單指矩陣乘積時,指的便是一般矩陣乘積。若A爲m×n矩陣,B爲n×p矩陣,則他們的乘積AB會是一個m×p矩陣。其乘積矩陣的元素如下面式子得出:
由於該算法很基礎,顯然運行速度比較慢,時間複雜度爲O(n3)。
4.2矩陣轉置
在C語言中,數組的存儲按照行優先規則,爲進一步利用cache,對矩陣乘法中的第二個矩陣進行裝置,提高命中率,進一步提高速度。Tmp作爲中間數組保存矩陣B裝置後的結果,運行時間在下文統一展示。
4.3分塊優化
核心是交叉乘加遍歷,當數組大小增加時,時間局部性會明顯降低,高速緩存中不命中數目增加。當使用分塊技術時,時間局部性由分塊大小來決定,而不是數組總大小來決定。本問題中選取塊大小爲bsize=4的方陣。
4.4矩陣轉置+openmp優化
爲進一步利用系統資源,在矩陣轉置的基礎上,進行openmp的初步優化。代碼呈現如下:
4.5 MPI並行
Mpi並行適用用數據規模較大,依賴性較弱的問題,如果採用更多的處理器,將會顯著提高計算效率。實際中,通常並不可能有像矩陣元素那麼多的處理器資源。這時我們該怎麼做。對於一個大小爲n × n 的大矩陣A,我們其實可以把它切分成s^2個子矩陣Ap,q,每個子矩陣的大小爲 m × m,其中 m = n / s,即0 <= p, q < s。對於兩個大矩陣A和B,現在我們有:
用圖象形象表示如下:
- 使用較簡單的MPI_Send和MPI_Recv實現
- 使用較高級的MPI_Scatter和MPI_Gather實現
具體代碼詳見c文件,文件中已加很多必要的註釋,不再贅述。
五、代碼文檔已開源至Github,歡迎投星支持。