《大規模並行處理器編程實戰》筆記

前言

這一篇文章主要是作爲我在看完《大規模並行處理器編程實戰》這本書之後的一個學習記錄。有些內容因爲我在上一篇博客已經記錄過了,這一篇就不做記錄了。

第六章 性能優化

6.1 更多關於線程執行的問題

GPU調度的基本單位爲warp,一般由32個thread組成。因爲GPU的執行方式爲SIMT(單指令多線程),也就是一條指令會被所有線程一起執行,等到這條指令被所有線程執行完,才執行下一條指令。所以如果warp中的thread存在很多條件分支,導致很多thread在等待某些thread執行分支內的指令,嚴重拖後程序的執行速度(舉個例子,就好比有一半thread要執行then裏的指令,有一半thread要執行else裏的指令。cuda會讓warp中的所有thread都執行then裏的指令,那些不需要執行then裏指令的thread的執行結果會被丟棄,但是它們還是要執行。也就是說,如果同一個warp中有很多分支的話,相當於warp中的thread要執行所有分支中的所有指令(不影響最後的正確結果),所以會執行比原本多很多的指令,效率很低)。因此同一個warp中,儘量不要有很多的分支,這樣程序的執行效率最高。


以上是兩種規約求和的方法,很明顯下圖的運算速度會更快。因爲下圖中每個warp存在很少的分支,而上圖中的warp中存在大量的分支。

6.2 全局存儲器的帶寬

全局存儲器通常採用DRAM來實現。根據DRAM的特性,地址連續的訪存請求可以進行合併,合併爲一個請求,相當於對全局存儲器的一次訪問可以取出多個連續的數據;而如果地址不連續的訪存請求,無法進行合併,相當於多次對全局存儲器提出訪存請求,很明顯這樣子訪存的效率會很低,因此儘量訪問連續的內存地址。這是書本上說的。
當然,我認爲跟cache塊的機制也有一定的關係。當訪問連續的內存單元時,一般都會在同一個cache塊上,這樣的訪問效率會很高,只需要讀一個cache塊;當訪問不連續的內存單元時,它們有可能分佈在多個cache塊中,這樣就需要訪問很多個cache塊,這樣的帶寬明顯就很低了。
而在共享存儲器中,因爲它是能實現高速的片上存儲器,則不存在這種合併訪存提高數據訪問速度的特性,因此我們可以先將全局存儲器中的數據按照連續地址單元的順序讀入共享存儲器中,然後在共享存儲器中可以隨機訪問,這不會影響訪問效率。

6.3 SM資源的動態劃分

SM中的執行資源包括寄存器、線程塊槽和線程槽。每個SM上的執行資源數目是限定的,但實際的線程塊和線程都是根據用戶設定的參數來分配的。如果每個塊中分配的線程多,則總的塊數就會比較小;塊中分配的線程少,則總的塊數就會比較多,這是一個動態的過程。所有塊的總線程數不能多於SM上限制的最大線程數。
此外,線程的數量還要受到寄存器數量的影響。SM上總的寄存器個數是一定的,它們會平均分給每一個線程。寄存器是線程私有的,每一個線程最少得對應一個寄存器,所以SM上總的線程數目不能夠大於SM上寄存器的總數。線程中的私有變量都是存在寄存器中。




上述例子中所謂的零開銷調度指的就是讓SM一直保持運行的狀態。因爲實際上SM上的運行單位爲warp,即在SM上每次只有一個warp處於運行的狀態。SM上的調度都是以warp爲單位進行調度。

6.4 數據預取

在GPU上,提高性能的關鍵在於避免計算核心(SM)空閒等待,保持計算核心的高效運算,也就是要讓SM一直處於執行的狀態。通過將一條需要好幾百個時鐘週期的訪存指令拆分成兩條,並在中間增加一些與訪存無關的指令,可以使SM一直保持運行的狀態。在存儲器訪問指令和已訪問的數據使用指令之間增加多條獨立指令,根據上一節的內容,這樣子會使得SM一直處在一個執行的狀態,使得吞吐量保持在一個較高的值。

上面通過在讀取global memory到shared memory中的這個過程中間,插入了獨立的計算指令,有效地隱藏延遲

6.5 指令混合

所謂的指令混合,其實就是將循環展開,這樣可以減掉一些分支指令、循環計數指令以及地址運算指令等,提高運行速度。

6.6 線程粒度

通常情況下,儘可能多地把更多工作放在每個線程中並採用更少的線程是有優勢的。其實就是讓每一個線程做更多的事,從而減少線程的數量(相對地減少)。這樣子有一個好處,可以減少訪存的次數,提高程序運行的速度。

6.7 小結


上圖中所說的塊指的是tile而不是block。也就是每次預取的數據塊的大小。(其實這個圖我也看不太懂)
技術:分塊,循環展開,數據預取,線程粒度
1:塊(tile)的大小在性能中起主要作用。
2:塊(title)的大小已經足夠大的情況下,循環展開和數據預取將更重要。
3:對於線程粒度而言,如果數據預取一個16*16的tile使用的register超過了SM中寄存器總數,那麼將會影響到其他優化的發揮。
4:各種優化的調整技術互相影響,要不斷結合找到最好的優化方法。

第十章

10.1 並行編程的目標

  1. 較短時間內解決給定的問題
  2. 給定時間內解決一些較大的問題
  3. 給定時間內對給定問題取得更好的解決方案

並行編程通常可以分爲4步:

  • 問題分解
  • 算法選擇
  • 語言實現
  • 性能調整

10.2 問題分解

首先要對問題進行理解、分析,判斷哪一些任務適合在主機上運行,哪一些任務適合在設備上運行。在設備上運行的任務,我們也需要考慮如何將它與CUDA的線程機制更好地結合起來,充分發揮CUDA的優勢。

Amdahl加速比公式
加速比S = 1 / (1 - a + a / n)
其中, a爲並行計算部分所佔比例,n爲並行處理結點個數

根據上面這個公式可以看出:並行計算受限於應用程序的串行部分,所以應用程序的加速有限。
在分解大型應用程序時,那些並不適合在CUDA設備上並行執行的小活動累加的執行時間,可能成爲最終用戶看到的限制加速的一個因素。

10.3 算法選擇

算法必須具備3個基本特點:

  • 確定性(definiteness):每一步都是準確陳述的,要執行的步驟中不存在任何多義性。
  • 有效的可計算性(effective computability):計算機可以實現其中的一步。
  • 有限性(finiteness):算法必須保證有終點。
    對於涉及矩陣的應用程序,分塊是取得高性能的重要算法策略之一。
    在計算過程中,可以通過稍微降低準確度,大幅度地提高網格算法的執行效率。

10.4 計算思想



這四部分的知識都是計算思想的基礎,學好了才能在設計並行計算算法方面有所建樹。

第十二章 結論與展望

存儲器帶寬是限制大規模並行計算系統性能的主要因素。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章