Warp
GPU的線程從thread grid 到threadblock,一個(gè)thread block在CUDA Core上執(zhí)行時(shí),會(huì)分成warp執(zhí)行,warp的顆粒度是32個(gè)線程。比如一個(gè)thread block可能有1024個(gè)線程,那么就會(huì)分成32個(gè)warp執(zhí)行。
上圖的CTA(cooperative thread arrays)即為thread block。
Warp內(nèi)的32個(gè)線程是以lock-step的方式鎖步執(zhí)行,也就是在沒(méi)有遇到分支指令的情況下,如果執(zhí)行,那么執(zhí)行的都是相同的指令。通過(guò)這種方式32個(gè)線程可以共享pc,源寄存器ID和目標(biāo)寄存器ID。
雖然warp是以32的顆粒度,但是具體在GPU內(nèi)部執(zhí)行時(shí),也可能是以16的顆粒度,分兩次執(zhí)行,比如早期的fermi架構(gòu)。
如上圖所示,兩個(gè)warp scheduler,每個(gè)warp每次只能在16個(gè)CUDA core上執(zhí)行。
后續(xù)的Pascal GPU架構(gòu) CUDA core增加到了32個(gè),每個(gè)周期都能執(zhí)行一個(gè)warp。
寄存器
GPU的寄存器數(shù)量是影響劃分CUDA thread block的數(shù)量的原因之一,如下圖所示[1]。
雖然內(nèi)部執(zhí)行是按照warp執(zhí)行的,按照調(diào)度順序和ready進(jìn)行調(diào)度。但是寄存器的分配是靜態(tài)的按照thread number分配的,而不是warp。在warp執(zhí)行時(shí),32個(gè)線程,每個(gè)線程讀取源寄存器,寫(xiě)入目標(biāo)寄存器。假設(shè)每個(gè)寄存器4B,那么每次32個(gè)線程讀取128B。
因而128B也就是GPU L1 Cache Cacheline的大小。不同于CPU,每一級(jí)的cache都要維護(hù)MOSEI的一致性,對(duì)于GPU的thread來(lái)說(shuō),私有memory不需要共享,因此對(duì)于local memory可以write back。而全局共享memory則可以write evict。
CPU的寄存器,在編譯器編譯時(shí),會(huì)根據(jù)寄存器的live time進(jìn)行優(yōu)化,而且在CPU內(nèi)部執(zhí)行時(shí),進(jìn)行重命名,在有限的寄存器數(shù)量上盡量地解決依賴問(wèn)題。
GPU只在編譯時(shí)優(yōu)化,盡量減少對(duì)memory的使用,在內(nèi)部執(zhí)行時(shí),如果針對(duì)每個(gè)warp都增加一個(gè)寄存器重命名單元,設(shè)計(jì)復(fù)雜。因此GPU每個(gè)線程需要的寄存器就是它編譯時(shí)需要的寄存器上限(寄存器上限也可以通過(guò)編譯器控制)。這就導(dǎo)致了實(shí)際GPU內(nèi)部執(zhí)行時(shí)對(duì)寄存器使用數(shù)量的波動(dòng)。如下圖[2]所示,因此也有很多文章研究如何優(yōu)化寄存器的使用。
在編譯時(shí),nvcc可以通過(guò)指定--maxrregcount指定寄存器的數(shù)量,但是過(guò)多的寄存器會(huì)因?yàn)楣潭ǖ募拇嫫髻Y源而導(dǎo)致thread數(shù)量變少,過(guò)少的寄存器也會(huì)導(dǎo)致需要頻繁的訪問(wèn)memory,因此也需要折衷。
WARP Divergence
之前討論warp時(shí)說(shuō)如果32個(gè)線程,沒(méi)有遇到分支,那么每個(gè)線程都執(zhí)行同一條指令,但是如果存在分支呢?
GPU沒(méi)有CPU的分支預(yù)測(cè),使用active mask和predicate register來(lái)構(gòu)建token stack來(lái)處理遇到分支時(shí)的問(wèn)題。
GPGPU-sim按照下圖[3]模擬的token stack,其中的
另一種可能的token stack則是按照如下的方式構(gòu)建,結(jié)合了指令,predicateregister和token stack。
上圖[4]中的(b)即為編譯出的匯編指令,SSY 0xF0即為push stack,if else分支指令結(jié)束重聚的指令地址為0xF0。每個(gè)warp會(huì)有當(dāng)前的active pc寄存器和active mask寄存器。我們假設(shè)一個(gè)warp內(nèi)有8個(gè)thread,在SSY0xF0指令執(zhí)行時(shí),會(huì)將active mask 壓棧,壓棧的內(nèi)容包括Fig1中的entry type SSY,active mask和re-convergence pc,也就是0xF0(從SSY 0xF0指令可以獲得).
在分支指令@PO BRA 0xB8執(zhí)行時(shí),會(huì)將DIV(divergence),activemask(0xF0,這個(gè)并非pc,而是active mask,當(dāng)前warp的每個(gè)thread的predicateregister拼接而成,8bit 每個(gè)bit表示一個(gè)thread是否滿足if條件)和 0xB8(if語(yǔ)句塊內(nèi)的第一條指令的地址)壓棧。
然后gpu會(huì)默認(rèn)執(zhí)行else分支(因?yàn)閕f需要跳轉(zhuǎn),else直接順序執(zhí)行),執(zhí)行else分支時(shí),需要對(duì)active mask取反,只執(zhí)行不滿足if條件的那些thread。
Else分支的最后一條匯編指令末尾會(huì)增加.S flag用于標(biāo)志popstack,此時(shí)pop指令會(huì)將active mask出棧,更新到active mask寄存器和active pc中,然后執(zhí)行if 分支,直到執(zhí)行完畢if內(nèi)的最后一條指令,對(duì)應(yīng)地址0xE8,此時(shí)再次出棧。
將當(dāng)前active pc更新為0xF0,activemask更新為0xFF,此時(shí)if else分支執(zhí)行完畢,回到重聚點(diǎn),所有線程繼續(xù)lock-step鎖步執(zhí)行。
這里只假設(shè)一個(gè)if else,但是實(shí)際上可能存在if else的嵌套,因此第一步SSY 0xF0,可以理解成上下文切換時(shí)的先保存當(dāng)前的activemask 0xFF和重聚點(diǎn)的指令地址0xF0。
上述的方案與GPGPU-sim中的架構(gòu)類似,除了在指令中顯式的增加了壓棧出棧,GPGPU-sim處理每一個(gè)分支都需要壓棧if else兩條分支,占用兩項(xiàng),而方案2)中每次除了保存當(dāng)前active mask外,只需壓棧一項(xiàng)。
審核編輯:劉清
-
寄存器
+關(guān)注
關(guān)注
31文章
5343瀏覽量
120401 -
編譯器
+關(guān)注
關(guān)注
1文章
1634瀏覽量
49136 -
CUDA
+關(guān)注
關(guān)注
0文章
121瀏覽量
13631 -
cache技術(shù)
+關(guān)注
關(guān)注
0文章
41瀏覽量
1064 -
GPU芯片
+關(guān)注
關(guān)注
1文章
303瀏覽量
5816
原文標(biāo)題:GPU Microarch 學(xué)習(xí)筆記【1】
文章出處:【微信號(hào):處理器與AI芯片,微信公眾號(hào):處理器與AI芯片】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論