天天看點

CUDA程式設計(五)關注記憶體的存取模式CUDA程式設計(五)

上一篇部落格我們使用thread完成了簡單的并行加速,雖然我們的程式運作速度有了50甚至上百倍的提升,但是根據記憶體帶寬來評估的話我們的程式還遠遠不夠,

除了通過block繼續提高線程數量來優化性能,這次想給大家先介紹一個訪存方面非常重要的優化,同樣可以大幅提高程式的性能~

大家知道一般顯示卡上的記憶體是 dram,是以最有效率的存取方式,是以連續的方式存取,單純說連續存取可能比較抽象,我們還是通過例子來看這個問題。

之前的程式,大家可以看到我們非常重要的核函數部分:

在計算立方和的部分,雖然看起來是連續存取記憶體位置(每個 thread 對一塊連續的數字計算平方和),但是實際上并不是這樣的,我們要考慮到實際上 thread 的執行方式。

前面提過,當一個 thread 在等待記憶體的資料時,gpu 會切換到下一個 thread。也就是說,實際上線程執行的順序是類似

是以,在同一個 thread 中連續存取記憶體,在實際執行時反而不是連續了,下圖很明顯的反應了這個問題,我們的存取是跳躍式的。

CUDA程式設計(五)關注記憶體的存取模式CUDA程式設計(五)

要讓實際執行結果是連續的存取,我們應該要讓 thread 0 讀取第一個數字,thread 1 讀取第二個數字…依此類推,很容易可以想象,通過這種存儲方式,我們取數字的時候就變成了連續存取。

CUDA程式設計(五)關注記憶體的存取模式CUDA程式設計(五)

根據我們上面的分析,我們原本的核函數并不是連續存取的,讀取數字完全是跳躍式的讀取,這會非常影響記憶體的存取效率,是以我們下一步要将取數字的過程變成:

thread 0 讀取第一個數字,thread 1 讀取第二個數字…

這點通過對核函數的for循環進行一個小修改就可以達到了~

通過上面對for循環的一個小修改就可以達到目的了,那麼這麼一個微小的修改到底有多大作用呢?

完整代碼 :

運作結果 :

CUDA程式設計(五)關注記憶體的存取模式CUDA程式設計(五)

我們看到這次運作用了894297個時鐘周期

不知道大家是否還記得上次我們用1024個線程運作的最終結果:6489302個時鐘周期,現在我們隻是很簡單的改了一下存取模式,同樣使用1024個線程最終隻使用了894297個時鐘周期

可以看到我們的速度居然提升了7.26倍,而我們隻是單純修改了一下存取模式罷了,是以我們可以看到連續存取這個存取優化還是十分重要的,在我們沒法再單純地從線程數量上繼續優化的情況下,從存取模式上進行的這個優化是十分有效的。

我們還是從記憶體帶寬的角度來進行一下評估:

首先計算一下使用的時間:

然後計算使用的帶寬:

資料量仍然沒有變 data_size 1048576,也就是1024*1024 也就是 1m

1m 個 32 bits 數字的資料量是 4mb。

是以,這個程式實際上使用的記憶體帶寬約為:

注意我們沒進行記憶體存取優化之前的記憶體帶寬是491mb/s,可以看到,我們通過這個優化一下子就把記憶體帶寬提升到了gb級别,不得不說這是一個非常令人滿意的效果,我們在沒有繼續增加線程數量的情況下,通過把記憶體的存取模式變成連續的,取得了7倍左右的加速。

這篇部落客要講解了通過如何盡可能的連續操作記憶體,減少記憶體存取方面的時間浪費。

通過最終的結果我們可以看到,看似不起眼的一個小改進(盡可能的去連續操作記憶體),竟然有這近7倍的性能提升,是以希望大家記住這個優化,在優化我們的cuda程式的時候,一定不要忘記從記憶體存取角度去進行一些優化,這往往能取得出乎意料的結果。

希望我的部落格能幫助到大家~

參考資料:《深入淺出談cuda》