CUDA學習--記憶體處理之暫存器(2)
1. 暫存器
GPU上一個SM可以看成一個多執行緒的CPU核。一般CPU擁有二、四、八個核。但一個GPU卻有N個SM核。但這裡需要注意的是,所有的工作都是有SM上的SP(流處理器)處理的。每個核上SP數目不同,因此每個核支援的執行緒數目也會有很大的不同。事實上,一個GPU裝置上的所有SM中活躍的執行緒數目通常數以萬計。
與CPU不同,GPU的每個SM(流多處理器)有上千個暫存器。CPU與GPU架構的一個主要區別就是CPU與GPU對映暫存器的方式。CPU通過使用暫存器重新命名和棧來執行多執行緒。為了執行一個新任務,CPU需要進行上下文切換,將當前所有暫存器的狀態儲存到棧上,然後從棧中恢復當前需要執行的新執行緒上次的執行狀態。這些操作需要花費上百個CPU時鐘週期。如果在CPU上開啟過多的執行緒,時間將主要花費在上下文的切換上。因此,如果在CPU上開啟過多的執行緒,有效工作的吞吐量將會快速降低
然而GPU卻恰恰相反。GPU利用多執行緒隱藏了記憶體獲取與指令執行帶來的延遲。GPU不使用暫存器重新命名機制,而是致力於為每一個執行緒都分配一個真實的暫存器(畢竟每個SM有上千個)。因此,當需要上下文切換時,所需要的操作就是將指向當前暫存器組的選擇器或指標更新,以指向下一個執行的執行緒束的暫存器組,開銷幾乎為零。從而可以看出,在GPU上開啟過少的執行緒反而會因為等待記憶體事務使GPU處於閒置狀態。(這裡用到的執行緒束概念,即同時排程的一組執行緒,包含32個執行緒)。
每個SM能夠排程若干個執行緒塊。在SM層,執行緒塊即若干個執行緒束的邏輯組。編譯時會計算出每個核心執行緒需要的暫存器數目
舉例來說,如果一個應用程式先前使用了4個執行緒塊,現在改用更多的暫存器,可能導致只有3個執行緒塊可供排程,這樣GPU的吞吐量將會降低1/4。
如果想使用暫存器來避免其他更慢的記憶體型別的使用,需要注意有效的使用它們。例如,一個迴圈根據一些布林變數依次設定某個值的每一位。高效的方法是將32個布林變數封裝到一個32位的字中,然後解封裝。可以寫這樣的一個迴圈,每次根據新的布林變數修改記憶體中的內容,做移位操作,移至字中正確的位置:
for(int i = 0; i < 31; i++)
{
packed_result |= (pack_array[i] << i);
}
如果變數packed_result存於記憶體中,則需要32次讀/寫記憶體操作。但如果將變數packet_result設定為區域性變數,編譯器會將其放入暫存器中,在暫存器中而不是在主存中做操作,然後將結果協會主記憶體中,因此可以節省31次記憶體讀/寫操作。