前面扯了很多,不過大多都是在講 CUDA 在軟體層面的東西;接下來,雖然 Heresy 自己也不熟,不過還是來研究一下硬體的部分吧~畢竟要最佳化的時候,好像還是要大概知道一下相關的東西的。這部分主要參考資料是:
在研究硬體架構前,可能須要先回去看《nVidia CUDA 簡介》,稍微回顧一下在 CUDA 中 thread、thread block、block grid 的意義:一個 CUDA 的平行化的程式會被以許多個thread 來執行,數個 thread 會被群組成一個 block,而多個 thread block 則會再構成 grid。也就是一個kernel 程式會有一個 grid,grid 底下有數個 block,每個 block 都是一群 thread 的群組。而在同一個block 中的 thread 可以透過 shared memory 來溝通,也可以同步。
硬體基本架構
對應到 CUDA
但是是否這些數字就是最合適的呢?其實也不盡然。因為實際上,一個 SM 可以允許的 block 數量,還要另外考慮到他所用到 SM的資源:shared memory、registers 等。在 G80 中,每個 SM 有 16KB 的 shared memory 和8192 個 register。而在同一個 SM 裡的 block 和 thread,則要共用這些資源;如果資源不夠多個 block使用的話,那 CUDA 就會減少 Block 的量,來讓資源夠用。在這種情形下,也會因此讓 SM 的 thread 數量變少,而不到最多的768 個。
比如說如果一個 thread 要用到 16 個 register 的話(在 kernel 中宣告的變數),那一個SM 的 8192 個 register 實際上只能讓 512 個 thread 來使用;而如果一個 thread 要用 32 個register,那一個 SM 就只能有 256 個 thread 了~而 shared memory 由於是 thread block共用的,因此變成是要看一個 block 要用多少的 shread memory、一個 SM 的 16KB 能分給多少個 block 了。
在研究硬體架構前,可能須要先回去看《nVidia CUDA 簡介》,稍微回顧一下在 CUDA 中 thread、thread block、block grid 的意義:一個 CUDA 的平行化的程式會被以許多個thread 來執行,數個 thread 會被群組成一個 block,而多個 thread block 則會再構成 grid。也就是一個kernel 程式會有一個 grid,grid 底下有數個 block,每個 block 都是一群 thread 的群組。而在同一個block 中的 thread 可以透過 shared memory 來溝通,也可以同步。
硬體基本架構
實際上在 nVidia 的 GPU 裡,最基本的處理單元是所謂的 SP(Streaming Processor),而一顆 nVidia 的 GPU 裡,會有非常多的 SP 可以同時做計算;而數個 SP 會在附加一些其他單元,一起組成一個 SM(Streaming Multiprocessor)。幾個 SM 則會在組成所謂的 TPC(Texture Processing Clusters)。
在G80/G92 的架構下,總共會有 128 個 SP,以 8 個 SP 為一組,組成 16 個 SM,再以兩個 SM 為一個 TPC,共分成8 個 TPC 來運作。而在新一代的 GT200 裡,SP 則是增加到 240 個,還是以 8 個 SP 組成一個 SM,但是改成以 3 個SM 組成一個 TPC,共 10 組 TPC。下面則是提供了兩種不同表示方式的示意圖。(可參考《NVIDIA G92終極狀態!!》、《NVIDIA D10U繪圖核心》)
對應到 CUDA
而在 CUDA 中,應該是沒有 TPC 的那一層架構,而是只要根據 GPU 的 SM、SP 的數量和資源來調整就可以了。SM 中的 Warp 和 Block
如果把 CUDA 的 Grid - Block - Thread 架構對應到實際的硬體上的話,會類似對應成 GPU - StreamingMultiprocessor - Streaming Processor;一整個 Grid 會直接丟給 GPU 來執行,而 Block大致就是對應到 SM,thread 則大致對應到 SP。當然,這個講法並不是很精確,只是一個簡單的比喻而已。
建議的數值?
CUDA 的 device 實際在執行的時候,會以 Block 為單位,把一個個的 block 分配給 SM 進行運算;而 block 中的 thread,又會以「warp」為單位,把 thread 來做分組計算。目前 CUDA 的 warp 大小都是 32,也就是 32 個 thread 會被群組成一個 warp來一起執行;同一個 warp 裡的 thread,會以不同的資料,執行同樣的指令。此外,在 Compute Capability 1.2的硬體中,還加入了 warp vote 的功能,可以快速的進行 warp 內的簡單統計。
基本上 warp 分組的動作是由 SM自動進行的,會以連續的方式來做分組。比如說如果有一個 block 裡有 128 個 thread 的話,就會被分成四組 warp,第 0-31個 thread 會是 warp 1、32-63 是 warp 2、64-95 是 warp 3、96-127 是 warp 4。
而如果 block 裡面的 thread 數量不是 32 的倍數,那他會把剩下的 thread 獨立成一個 warp;比如說 thread數目是 66 的話,就會有三個 warp:0-31、32-63、64-65。由於最後一個 warp 裡只剩下兩個thread,所以其實在計算時,就相當於浪費了 30 個 thread 的計算能力;這點是在設定 block 中 thread數量一定要注意的事!
一個 SM 一次只會執行一個 block 裡的一個 warp,但是 SM 不見得會一次就把這個 warp的所有指令都執行完;當遇到正在執行的 warp 需要等待的時候(例如存取 global memory 就會要等好一段時間),就切換到別的warp 來繼續做運算,藉此避免為了等待而浪費時間。所以理論上效率最好的狀況,就是在 SM 中有夠多的 warp可以切換,讓在執行的時候,不會有「所有 warp 都要等待」的情形發生;因為當所有的 warp 都要等待時,就會變成 SM 無事可做的狀況了~
下圖就是一個 warp 排程的例子。一開始是先執行 thread block 1 的warp1,而當他執行到第六行指令的時候,因為需要等待,所以就會先切到 thread block 的 warp2來執行;一直等到存取結束,且剛好有一個 warp 結束時,才繼續執行 TB1 warp1 的第七行指令。
實際上,warp 也是 CUDA 中,每一個 SM 執行的最小單位;如果 GPU 有 16 組 SM 的話,也就代表他真正在執行的 thread數目會是 32*16 個。不過由於 CUDA 是要透過 warp 的切換來隱藏 thread的延遲、等待,來達到大量平行化的目的,所以會用所謂的 active thread 這個名詞來代表一個 SM 裡同時可以處理的 thread數目。
而在 block 的方面,一個 SM 可以同時處理多個 thread block,當其中有 block 的所有thread 都處理完後,他就會再去找其他還沒處理的 block 來處理。假設有 16 個 SM、64 個 block、每個 SM可以同時處理三個 block 的話,那一開始執行時,device 就會同時處理 48 個 block;而剩下的 16 個 block 則會等SM 有處理完 block 後,再進到 SM 中處理,直到所有 block 都處理結束。
在 Compute Capability 1.0/1.1 中,每個 SM 最多可以同時管理 768 個 thread(768 active threads)或 8 個 block(8active blocks);而每一個 warp 的大小,則是 32 個 thread,也就是一個 SM 最多可以有 768 / 32 =24 個 warp(24 active warps)。到了 Compute Capability 1.2 的話,則是 active warp則是變為 32,所以 active thread 也增加到 1024。
在這裡,先以 Compute Capability1.0/1.1 的數字來做計算。根據上面的數據,如果一個 block 裡有 128 個 thread 的話,那一個 SM 可以容納 6 個block;如果一個 block 有 256 個 thread 的話,那 SM 就只能容納 3 個 block。不過如果一個 block 只有64 個 thread 的話,SM 可以容納的 block 不會是 12 個,而是他本身的數量限制的 8 個。
因此在Compute Capability 1.0/1.1 的硬體上,決定 block 大小的時候,最好讓裡面的 thread 數目是 warp數量(32)的倍數(可以的話,是 64 的倍數會更好);而在一個 SM 裡,最好也要同時存在複數個 block。如果再希望能滿足最多 24 個warp 的情形下,block 裡的 thread 數目似乎會是 96(一個 SM 中有 8 個 block)、128(一個 SM 中有 6個 block)、192(一個 SM 中有 4 個 block)、256(一個 SM 中有 3 個 block) 這些數字了~
而官方的建議則是一個 block 裡至少要有 64 個 thread,192 或 256 個也是通常比較合適的數字(請參考 Programming Guide)。
但是是否這些數字就是最合適的呢?其實也不盡然。因為實際上,一個 SM 可以允許的 block 數量,還要另外考慮到他所用到 SM的資源:shared memory、registers 等。在 G80 中,每個 SM 有 16KB 的 shared memory 和8192 個 register。而在同一個 SM 裡的 block 和 thread,則要共用這些資源;如果資源不夠多個 block使用的話,那 CUDA 就會減少 Block 的量,來讓資源夠用。在這種情形下,也會因此讓 SM 的 thread 數量變少,而不到最多的768 個。
比如說如果一個 thread 要用到 16 個 register 的話(在 kernel 中宣告的變數),那一個SM 的 8192 個 register 實際上只能讓 512 個 thread 來使用;而如果一個 thread 要用 32 個register,那一個 SM 就只能有 256 個 thread 了~而 shared memory 由於是 thread block共用的,因此變成是要看一個 block 要用多少的 shread memory、一個 SM 的 16KB 能分給多少個 block 了。
所以雖然說當一個 SM 裡的 thread 越多時,越能隱藏 latency,但是也會讓每個 thread 能使用的資源更少。因此,這點也就是在最佳化時要做取捨的了。
文章转自:http://www.opengpu.org/forum.php?mod=viewthread&tid=1552