CUDA版本稀疏矩陣向量乘

URNOTJANET發表於2017-12-27

說明:

1.轉載請聯絡本人

2.程式碼在最後

問題描述

SpMV在許多科學計算程式中都有廣泛的應用。
資料矩陣A是稀疏的,輸入向量x和輸出向量y是稠密的。

公式: y = Ax

實驗要求

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

方法

CUDA稀疏矩陣上沒有通用的方法,一般來說對於SpMV問題有兩個可以著手的點:
一個是儲存方式,另一個是矩陣相乘的方式。
矩陣相乘部分可以參考上一個實驗中一些常用的方法,而在稀疏矩陣的問題中,尤為突出的是儲存方式的選取。通常來說有COO,CSR,DIA,ELL,HYB等方法,較常採用的是COO與CSR。

實驗

1.實驗環境

本機: CPU:i5-4210U
記憶體:8G OS: Windows10 1607

實驗室:CPU: i7-7700K GPU:GTX 1080
記憶體:8G DDR4 OS:Ubuntu 16.04

2.結果及分析

假設稀疏矩陣中不為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

沒有達到理論峰值的原因是:
程式並不只是在做浮點數運算或只是在訪問記憶體;
儲存格式的問題;
可能記憶體頻寬利用效果不好;
細顆粒度不夠高;快取設定不夠;
以及作業系統的執行緒排程,和伺服器本身的不穩定性等等。

優化嘗試

2.1嘗試ELL儲存格式

在採用該格式之後,效能提升非常顯著,在老師提供的原始碼基礎上,GFlops/s從1左右直接提升至18。
程式碼存放在路徑 但ELL儲存格式受到最大列數的制約,雖然在這次實驗中體現不明顯,但理論上如果原矩陣存在著一行全是數,而其餘行只有幾個數的情況下,儲存空間會非常浪費,效能可能會比CSR要差。

2.2嘗試採用for迴圈展開

在spmv函式中的for迴圈之前,使用#pragma unroll,GFLOPS/s不升反降,不清楚原因。所以for展開的效果不是很顯著。

2.3 嘗試使用texture cache,行對齊等

在CSDN 上參考了一份程式碼,其中涉及到多個效能的優化策略,其程式碼中採用的是CSR的儲存格式,報告顯示對於非零元素較大的稀疏矩陣有著很好的加速效果。
在本次實驗中,由於矩陣初始化時資料分配也不很符合該程式碼的要求,所以加速效果不夠明顯。
對應程式碼放在文末連結。

結論

  1. 普通矩陣和稀疏矩陣的優化思路相差很大,而稀疏矩陣在實際生活中運用最為廣泛。
  2. shared memory的正確使用能夠非常顯著地提升矩陣乘法的效能
  3. 選取一個好的儲存格式,對效能的提示非常的關鍵
  4. CUDA中存在一個cusparse庫可以直接呼叫來計算SpMV,但是執行時必須考慮到CUDA存矩陣的方式(按列存取)

參考

[1]《稀疏矩陣儲存格式總結+儲存效率對比:COO,CSR,DIA,ELL,HYB》
[2]《CUDA實現稀疏大矩陣乘法》
  注:這是採用了CSR儲存及texture方式優化的程式碼
[3]《稀疏矩陣儲存格式總結+儲存效率對比》

程式碼地址

個人GitHub:Icarusintheworld

相關文章