OpenMP 版本稀疏矩陣向量乘

URNOTJANET發表於2017-12-27

說明:

1.轉載請聯絡本人

2.程式碼在最後

問題描述

稀疏矩陣向量乘法(Sparse Matrix-Vector Multiplication, SpMV)在許多科學計算程式中都有廣泛的應用。
如下所示,資料矩陣A是稀疏的,輸入向量x和輸出向量y是稠密的。

y = Ax

要求

  1. 根據記憶體大小測不同規模和不同稀疏程度的稀疏矩陣的處理速度(GFLOPS/s)和頻寬(GB/s),並給出計算公式。
  2. 請計算系統的理論峰值,如果沒有達到理論峰值,嘗試給出原因。

思路和方法

老師在/hw2目錄下提供的程式碼已經能夠達到比較好的儲存訪問了,所以在這個基礎上新增OpenMP程式碼以及嘗試其他的優化,如迴圈優化即可。

實驗

結果及分析

不同規模和不同稀疏程度矩陣處理速度公式:
假設稀疏矩陣中不為0的個數為k
處理速度公式 = 2*k/1000000000/time
頻寬計算公式 = (sizeof(int)*dim + sizeof(int)*k + sizeof(float)*k + sizeof(float)*dim*2)/1000000000/time

系統理論峰值(即浮點數理論峰值)
叢集理論浮點峰值
= CPU主頻(GHz)× CPU每時鐘週期執行浮點運算次數 × 節點數 × 8(每節點雙路四核)
=4.2*4*8=134.4GFLOPS/s

峰值頻寬: B=F×D/8=2133MHz*64bit/8=17.064GHz
沒有達到理論峰值的原因是,程式並不只是在做浮點數運算或只是在訪問記憶體,以及作業系統的執行緒排程,和伺服器本身的不穩定性等等...

1.嘗試parallel shared/private以及dynamic

問題1: 發現在initMatrix裡面加omp效果不佳.
解決: 可能會因為並行訪問同一數值,導致訪問錯誤和陣列越界等問題有關

問題2: 發現使用shared/private之後速度較慢,GFLOPS較低
解決: 與privaet是否使用無關,主要是這樣會導致並行區域開多,速度會下降

2.嘗試parallel guided 和減少for迴圈依賴
程式碼如下

for(int i=1; i<=dim; i++)
     tmp[i-1] = row[i]; // 迴圈分離,減少依賴
for(int numOfTimes=0; numOfTimes<ITERATIONS; numOfTimes++){
  #pragma omp parallel for num_threads(thread_num)schedule(guided)
      for(unsigned short i=0; i<dim; i++){
        float t = 0;
        for(int j=row[i]; j<tmp[i]; j++){
          unsigned short colNum = col[j];
          t += data[j] * vec[colNum];
        }
        result[i] = t;
      }}
複製程式碼

得到的資料結果如下: (執行緒數量:10,結果是實驗三次取平均)

Dim規模 GFLOPS/s (CSR)GB/s Time(s)
500 0.084 0.44 0.000063
1000 0.505 2.32 0.000040
5000 2.393 9.86 0.000209
10000 2.598 10.55 0.000770
20000 2.544 10.25 0.003087
30000 2.509 10.09 0.007173
40000 2.454 9.85 0.013037

3.嘗試採用三元組進行測試

結果:得到的速度較CSR儲存方式的速度要慢,雖然程式碼設計較CSR簡單

4.將部分變數改為unsigned short

結論

1.使用OpenMP中guided進行for迴圈排程能達到最好的效果。
2. CSR的儲存格式較三元組的儲存格式更優,訪問更快。
3. 採用迴圈分離等優化方式能夠提高GFLOPS的大小。
4. 變數如果提前定義,有可能會得出錯誤的結果。另外private的呼叫也會降低GFLOPS的大小

參考

[1]《稀疏矩陣儲存格式總結+儲存效率對比:COO,CSR,DIA,ELL,HYB》
[2]《稀疏矩陣向量乘法》

程式碼地址

個人GitHub:Icarusintheworld

相關文章