cuda程式優化的最終目的,是以最短的時間,在允許的誤差範圍內完成給定的計算任務。在這裡,「最短的時間」是指整個程式的執行時間,更側重於計算的吞吐量,而不是單個資料的延遲。在開始考慮使用gpu和cpu協同計算之前,應該先粗略地評估使用cuda是否能達到預想的效果,包括以下幾個方面:
1.精度
目前,gpu的單精度計算效能要遠遠超過雙精度計算效能,整數乘法、除法、求模等運算的指令吞吐量也較為有限。在科學計算中,由於需要處理的資料量巨大,往往只有在採用雙精度或者四精度時才能獲得可靠的結果。目前,採用tesla架構的gpu還不能很好地滿足高精度計算的需求。如果應用程式需要很高的精度,或者需要進行多輪迭代,建議只在關鍵的步驟中使用雙精度,而在其他部分仍然使用單精度浮點以獲得指令吞吐量和精度的平衡。如果應用程式對精度有更高的要求,那麼現在的架構還不能獲得太高的加速比。不過,在2023年將會普及的下一代架構中,雙精度浮點和整數處理能力將有很大的提公升,這種情況會有根本性的改變。
2.延遲
目前,cuda還不能單獨為某個處理核心分配任務,因此必須先緩衝一定量的資料,再交給gpu進行計算。這樣的方式可以獲得很高的資料吞吐量,不過單個資料經過緩衝、傳輸到gpu計算、再拷貝回記憶體的延遲就比直接由cpu進行序列處理要長很多。如果對應用實時性要求很高,比如必須在數十微秒內完成對乙個輸入的處理,那麼使用cuda可能會影響系統的整體效能。對於要求實現人機實時互動的系統,應該將延遲控制在數十毫秒的量級,以及時響應使用者的輸入。通過減小緩衝,可以減小延遲,但緩衝的大小至少應該保證每個核心程式處理的一批資料能夠讓gpu滿負荷工作。在大多數情況下,如果應用要求的計算吞吐量大到需要由中高階gpu才能實時實現,那麼在投入相同成本的前提下,是很難使用cpu相近效果的。如果確實對實時性和吞吐量都有很高要求,應該考慮asic, fpga或者dsp實現,這需
要更多的投入,更長的開發時間和硬體開發經驗。
3.計算量
如果計算量太小,那麼使用cuda是不划算的。衡量計算量有絕對和相對兩種方式。
從絕對量來說,如果待優化的程式使用頻率比較低,並且每次呼叫需要的時間也可以接受,那麼使用cuda優化並不會顯著改善使用體驗。對於一些計算量非常小(整個程式在cpu上可以在幾十毫秒內完成)的應用來說,使用cuda計算時在gpu上的執行時間無法隱藏訪存和資料傳輸的延遲,此時整個應用程式需要的時間反而會比cpu更長。此外,雖然gpu的單精度浮點處理能力和視訊記憶體頻寬都遠遠超過了cpu,但由於gpu使用pci-e匯流排與主機連線,因此它的輸入和輸出的吞吐量受到了io頻寬的限制。當要計算的問題的計算密集度很低時,執行計算的時間遠遠比io花費的時間短,那麼整個程式的瓶頸就會出現在pci-e頻寬上。此時無論如何提高浮點處理能力和視訊記憶體頻寬,都無法提高系統效能。tesla c1060頻寬與延遲的比較如圖4-1所示。
從相對計算量來說,如果可以並行的部分在整個應用中所佔的比例不大,那麼gpu對程式整體效能的提高也不會非常明顯。如果整個應用中序列部分占用時間較長,而並行部分較短,那麼也需要考慮是否值得使用gpu進行平行計算。例如,假設乙個程式總的執行時間為1.0,其中序列部分佔0.8,而並行部分只佔0.2,那麼使用gpu將並行部分加速10倍,總的執行時間也只能從1.0降低到0.82。即使是在cpu和gpu可以同時平行計算的應用中,執行時間
也至少是cpu序列計算需要的0.8。只有在平行計算占用了絕大多數計算時間的應用中,使用cuda加速才能獲得很高的加速比。不過,隨著gpu+cpu平行計算的普及和gpu架構的進一步改進,未來可能即使只能獲得較小的加速比,也會由gpu來執行更多的計算任務。
完成對gpu加速效果的粗略評估後,就可以開始著手編寫程式了。為了在最短的時間內完成計算,需要考慮演算法、並行劃分、指令流吞吐量、儲存器頻寬等多方面因素。總的來說,
優秀的cuda程式應該同時具有以下幾個特徵:
在給定的資料規模下,選用演算法的計算複雜度不明顯高於最優的演算法。
active warp的數量能夠讓sm滿載,並且active block數量大於2,能夠有效的隱藏訪存延遲。
當瓶頸出現在指令流(主要是運算)時,指令流的效率己經經過了充分優化。
當瓶頸出現在訪存或者io時,程式己經選用了恰當的儲存器來儲存資料,並使用了適當的儲存器訪問方式,以獲得最大頻寬。
按照開發流程的先後順序,cuda程式的編寫與優化需要解決以下問題:
1)確定任務中的序列部分和並行部分,選擇合適的演算法。首先,需要將問題分為幾個步驟,並確定哪些步驟可以用並行演算法實現,並確定要使用的演算法。
2)按照演算法確定資料和任務的劃分方式,將每個需要並行實現的步驟對映為乙個滿足cuda兩層並行模型的核心函式。在這裡就要盡量讓每個sm上擁有至少6個活動warp和至少2個活動執行緒塊。
3)編寫乙個能夠正確執行的程式,作為優化的起點。程式必須能夠穩定執行,不能發生儲存器洩漏的情況。為了保證結果正確,在必要的時候必須使用memory fence、同步、原子操作等功能以及volatile關鍵字。在精度不足或者發生溢位時必須使用雙精度浮點或者更長的整數型別。
4)優化視訊記憶體訪問,避免視訊記憶體頻寬成為瓶頸。在視訊記憶體頻寬得到完全優化前,其他優化不會產生明顯結果。視訊記憶體訪問優化中可以使用的技術包括:
可以採用相同的block和grid維度實現的幾個kernel合併為乙個,減少對視訊記憶體的訪問。
除非非常必要,應該盡力避免將執行緒私有變數分配到local memoryo
為滿足合併訪問,採用cudamallocpitch()或者cudamalloc3d()分配視訊記憶體。
為滿足合併訪問,對資料型別進行對齊(使用_align)o
為滿足合併訪問,保證訪問的首位址從16的整數倍開始,如果可能,盡量讓每個執行緒一次讀的資料字長都為32bita
在資料只會被訪問一次,並且滿足合併訪問的情況下可以考慮使用zerocopya
在某些情況下,考慮儲存器控制器負載不均衡造成分割槽衝突的影響。
使用擁有快取的常數儲存器和紋理儲存器提高某些應用的實際頻寬。
5)優化指令流。在編譯過程中,編譯器對**會進行一些優化。但是程式設計師很難直接控制編譯器對**的優化,所以指令流優化不一定能獲得立竿見影的效果。但是,仍然有一些準則可以參考,包括:
如果只需要少量執行緒進行操作,一定記得要使用類似「if threadid < n」的方式,避免多個執行緒同時執行占用更長時間或者產生錯誤結果。
在不會出現不可接受的誤差的前提下採用cuda算術指令集中的快速指令。
使用#unroll,讓編譯器能夠有效地展開迴圈。
採用原子函式實現更加複雜的演算法,並保證結果的正確性。
避免多餘的同步。
如果不產生bank conflict的演算法不會造成演算法效率的下降或者非合併訪問,就應該避免bank conflicto
6)資源均衡。調整shared memory和register的使用量。為了使程式能夠獲得更高的sm佔用率,應該調整每個執行緒處理的資料數量、shared memory和register的使用量。這需要在三者間進行調整。當執行緒處理的子任務間有一些完全相同的部分時,應該只使用少量執行緒來完成公共部分的計算,再將公用資料通過shared memory廣播給所有執行緒。為了獲得更高的sm佔用率,必須控制每個執行緒的shared memory和register的使用量。通過調整block大小,修改演算法和指令,以及動態分配shared memory,都可以提高shared的使用效率。而減小register的使用則相對困難,因為register的使用量並不是由核心程式中宣告的變數多少決定,而是由核心程式中使用暫存器最多的時刻的用量決定的。由於編譯器會盡量減小暫存器的用量,因此實際使用的暫存器有可能會小於在程式中宣告的量。但是在通常情況下,由於需要暫存中間結
果並且一些指令也需要更多的暫存器,一般暫存器用量都大於核心程式中宣告的私有變數的總數量。使用以下方法可能可以節約一些暫存器的使用:使用shared memory儲存變數;使用括號更加明確地表示每個變數的生存週期;用對[u] long型的處理代替對兩個相鄰的[u]short型或者四個相鄰的[u]char型的處理;使用占用暫存器較小的等效指令代替原有指令,如用sin函式代替sin函式。不過,由於不能對編譯器的優化過程進行控制,即使使用了這些手段也不一定能減小暫存器的用量。值得注意的是,採用一maxrregcount編譯選項只是讓編譯器將超出限制的私有暫存器分配在local memory中,造成較大的訪存延遲。
7)與主機通訊優化。由於pci-e頻寬相對較小,應該儘量減少cpu與gpu間傳輸的資料量,並通過一些手段提高可用頻寬。可用的技術包括:
使用cudamallochost分配主機端儲存器,可以獲得更大的頻寬。
一次快取較多的資料,再一併傳輸,可以獲得較高的實際頻寬。
需要將結果顯示到螢幕時,直接使用與圖形學api互操作功能完成,避免將資料返回。
使用流和非同步處理隱藏與主機的通訊時間。
使用zero-copy技術和write-combined memory提高可用頻寬。
對於用cuda c語言編寫的程式,按照上述流程進行優化是比較適合的。不過在優化中,各種因素往往相互制約,很難同時達到最優。讀者需要按照要處理問題的型別、瓶頸出現的部位和原因具體分析。按照預想進行優化也不是總能達到預想中的效果,有時優化手段反而會降低效能。在實踐中,仍然需要不斷實驗各種優化方法,在不斷試驗與迭代中一步步排除不可行的方案,最後得到乙個比較理想的方案。
使用cuda c並不總是能夠編譯到最優的指令。如果確實必要,可以用ptx優化程式中最關鍵的步驟。
除此以外,還要靈活採用巨集和模板,動態分配記憶體和視訊記憶體以及動態劃分資料等手段提高程式的通用性,並在處理不同規模、不同資料型別的問題時選用不同的優化策略。
在2023年將要推出的新處理器中,各種儲存器的頻寬和延遲會有一定的調整,大部分指令的吞吐量也會有非常顯著的提公升。隨著gpu架構的進一步改進和編譯器效能的不斷提高,下一代gpu上的cuda程式優化工作會變得更加簡單。
第1個CUDA程式
vs2013 cuda6.5編譯環境搭建 1.安裝vs2013 2.安裝cuda6.5 可能遇到的問題 缺少intel的驅動,vs版本太低 建立第1個cuda程式 1.建立win32空專案 2.新增依賴項 3.開啟專案屬性頁 4.開啟配置屬性中的vc 目錄,在包含目錄中加入 cuda inc pat...
CUDA筆記1 share memory優化
share memory demo 實現c mh,nw a mh,mw b mw,nw include include include define n 8 a b c為方陣時的矩陣寬度 非方陣的設定如下 define mh 16 a的行數 define mw 32 a的列數 b的行數 define...
mysql優化 1。之概述
設計 儲存引擎,字段型別,正規化。功能 索引,快取,分割槽。架構 主從複製,讀寫分離,負載均衡。合理的sql 測試和經驗。表引擎 某一種資料的儲存格式。一般叫某一種檔案系統。一般用到四種引擎innodb,myisam archive memory innodb 事務 多條sql語句全部執行完畢。才算...