說明:
1.轉載請聯絡本人
2.程式碼在最後問題描述
稀疏矩陣向量乘法(Sparse Matrix-Vector Multiplication, SpMV)在許多科學計算程式中都有廣泛的應用。
如下所示,資料矩陣A是稀疏的,輸入向量x和輸出向量y是稠密的。
y = Ax
要求
- 根據記憶體大小測不同規模和不同稀疏程度的稀疏矩陣的處理速度(GFLOPS/s)和頻寬(GB/s),並給出計算公式。
- 請計算系統的理論峰值,如果沒有達到理論峰值,嘗試給出原因。
思路和方法
老師在/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