由于最近實(shí)驗(yàn)需要在GPU上狠下功夫,所以惡補(bǔ)了GPU的知識(shí)。我發(fā)現(xiàn)國(guó)內(nèi)blog其實(shí)對(duì)GPU內(nèi)部的物理、邏輯的各個(gè)組件、對(duì)存儲(chǔ)單元等總結(jié)的并不是特別完備,所以我根據(jù)自己的理解以及他人的博客中的內(nèi)容自己總結(jié)了一份GPU相關(guān)知識(shí)點(diǎn),用來(lái)幫助自己回顧并普及知識(shí)。
本文將從GPU的物理結(jié)構(gòu)、邏輯執(zhí)行結(jié)構(gòu)以及存儲(chǔ)模塊三個(gè)角度進(jìn)行講述。
如有敘述不完備的地方,請(qǐng)讀者批評(píng)指正,我會(huì)將不正確的地方進(jìn)行修改。
一、GPU發(fā)展
能看這篇文章的人相比對(duì)GPU是有一定的基礎(chǔ)了解的,所以我就直接開(kāi)始從干貨開(kāi)始講起。
NVIDIA GPU架構(gòu)歷經(jīng)多次變革,從起初的Tesla發(fā)展到最新的Turing架構(gòu),發(fā)展史可分為以下時(shí)間節(jié)點(diǎn):
- 2008 - Tesla
Tesla最初是給計(jì)算處理單元使用的,應(yīng)用于早期的CUDA系列顯卡芯片中,并不是真正意義上的普通圖形處理芯片。
- 2010 - Fermi
Fermi是第一個(gè)完整的GPU計(jì)算架構(gòu)。首款可支持與共享存儲(chǔ)結(jié)合純cache層次的GPU架構(gòu),支持ECC的GPU架構(gòu)。
- 2012 - Kepler
Kepler相較于Fermi更快,效率更高,性能更好。
- 2014 - Maxwell
其全新的立體像素全局光照 (VXGI) 技術(shù)首次讓游戲 GPU 能夠提供實(shí)時(shí)的動(dòng)態(tài)全局光照效果?;?Maxwell 架構(gòu)的 GTX 980 和 970 GPU 采用了包括多幀采樣抗鋸齒 (MFAA)、動(dòng)態(tài)超級(jí)分辨率 (DSR)、VR Direct 以及超節(jié)能設(shè)計(jì)在內(nèi)的一系列新技術(shù)。
- 2016 - Pascal
Pascal 架構(gòu)將處理器和數(shù)據(jù)集成在同一個(gè)程序包內(nèi),以實(shí)現(xiàn)更高的計(jì)算效率。1080系列、1060系列基于Pascal架構(gòu)
- 2017 - Volta
Volta 配備640 個(gè)Tensor 核心,每秒可提供超過(guò)100 兆次浮點(diǎn)運(yùn)算(TFLOPS) 的深度學(xué)習(xí)效能,比前一代的Pascal 架構(gòu)快5 倍以上。
- 2018 - Turing
Turing 架構(gòu)配備了名為 RT Core 的專用光線追蹤處理器,能夠以高達(dá)每秒 10 Giga Rays 的速度對(duì)光線和聲音在 3D 環(huán)境中的傳播進(jìn)行加速計(jì)算。Turing 架構(gòu)將實(shí)時(shí)光線追蹤運(yùn)算加速至上一代 NVIDIA Pascal? 架構(gòu)的 25 倍,并能以高出 CPU 30 多倍的速度進(jìn)行電影效果的最終幀渲染。2060系列、2080系列顯卡也是跳過(guò)了Volta直接選擇了Turing架構(gòu)。
我們常用的2080Ti就是圖靈架構(gòu)-https://baijiahao.baidu.com/s?id=1612181658006920092&wfr=spider&for=pc。
然而這些架構(gòu)與架構(gòu)之間有很多相同點(diǎn),但是為了提高性能,也設(shè)計(jì)了不同的組件組合方式,所以針對(duì)同一個(gè)CUDA代碼,為了達(dá)到最優(yōu)的性能,不同架構(gòu)的GPU上有著不同的核數(shù)配比。
二、GPU的物理結(jié)構(gòu)
我們這里主要講述Nvidia廠家的GPU結(jié)構(gòu)。
這里大部分內(nèi)容參考了:https://www.cnblogs.com/timlly/p/11471507.html。
在這里我們需要整體對(duì)GPU有一個(gè)認(rèn)識(shí),即GPU的物理結(jié)構(gòu)包括哪些組件呢?
從硬件角度講,一個(gè)GPU由多個(gè)SM組成,一個(gè)SM包含有多個(gè)SP(線程)(以及還有寄存器資源,shared memory資源,L1cache,scheduler,SPU,LD/ST單元等等)。那這些組件各個(gè)之間的關(guān)系又是什么樣子的呢?
1 最早的Tesla架構(gòu)

Tesla微觀架構(gòu)總覽圖如上。下面將闡述它的特性和概念:
擁有7組TPC(Texture/Processor Cluster,紋理處理簇)
每個(gè)TPC有兩組SM(Stream Multiprocessor,流多處理器)
每個(gè)SM包含:
- 8個(gè)SP(Streaming Processor,流處理器)
- 2個(gè)SFU(Special Function Unit,特殊函數(shù)單元)
- L1緩存、MT Issue(多線程指令獲?。-Cache(常量緩存)、共享內(nèi)存
- 除了TPC核心單元,還有與顯存、CPU、系統(tǒng)內(nèi)存交互的各種部件。
在該GPU中,最核心的執(zhí)行單元為SP,即真正的勞動(dòng)者。本章節(jié)我們只關(guān)心物理結(jié)構(gòu),具體的存儲(chǔ)對(duì)比我們放到下面進(jìn)行講述。
2 Fermi架構(gòu)
由于Tesla比較久遠(yuǎn),我們就不詳細(xì)說(shuō)明,下面為Fermi架構(gòu)

Fermi架構(gòu)如上圖,它的特性如下:
擁有16個(gè)SM
每個(gè)SM:
2個(gè)Warp(線程束)
兩組共32個(gè)Core
16組加載存儲(chǔ)單元(LD/ST)
4個(gè)特殊函數(shù)單元(SFU)
- 每個(gè)Warp:
16個(gè)Core
Warp編排器(Warp Scheduler)
分發(fā)單元(Dispatch Unit)
- 每個(gè)Core:
1個(gè)FPU(浮點(diǎn)數(shù)單元)
1個(gè)ALU(邏輯運(yùn)算單元)
通過(guò)左圖我們能看出,每一個(gè)綠色的大block為一個(gè)SM,上面8個(gè)下面8個(gè)共16個(gè)SM。右圖為SM內(nèi)部結(jié)構(gòu),包括了16 * 2個(gè)core(真正用于計(jì)算的單元),上面是32768個(gè)寄存器,可被32個(gè)core所使用,上面有兩組調(diào)度器以及分發(fā)單元,理論上兩個(gè)調(diào)度器可以同時(shí)調(diào)度兩個(gè)warp,而該架構(gòu)下一個(gè)warp只能運(yùn)行16個(gè)線程,后面的架構(gòu)會(huì)變成32個(gè)。
3 Maxwell架構(gòu)

該架構(gòu)擁有四個(gè)GPC(Graphics Processing Cluster),每個(gè)GPC擁有多個(gè)SM(SMX、SMM)。當(dāng)然不同架構(gòu)下每個(gè)GPC的SM數(shù)量是不等的。例如,GM204有4個(gè)GPC,每個(gè)GPC有4個(gè)SM,但Tegra X1有1個(gè)GPC和2個(gè)SM,它們均采用Maxwell設(shè)計(jì)。
上圖中的GPU為GM204,擁有4個(gè)GPC,圖中一個(gè)GPC中包括4個(gè)SMM(Maxwell SM)。每組SMM單元又由4個(gè)小單元組成,每組32個(gè)CUDA核心

4 NVidia Kepler架構(gòu)


Kepler除了在硬件有了提升,有了更多處理單元之外,還將SM升級(jí)到了SMX。該架構(gòu)包括:
15個(gè)SM
6個(gè)64位memory controller
192個(gè)單精度CUDA cores,64個(gè)雙精度單元,32個(gè)SFU,32個(gè)load/store單元(LD/ST)
增加register file到64K
每個(gè)Kepler的SM包含四個(gè)warp scheduler、八個(gè)instruction dispatchers,使得每個(gè)SM可以同時(shí)issue和執(zhí)行四個(gè)warp。
Kepler K20X(compute capability 3.5)每個(gè)SM可以同時(shí)調(diào)度64個(gè)warp共計(jì)2048個(gè)thread。
分析一下上面的指標(biāo),總體來(lái)說(shuō),SM就是SMX,一個(gè)SMX中有192個(gè)core,有四個(gè)調(diào)度器使得每次可以執(zhí)行四個(gè)warp,大大提高了執(zhí)行效率。
5 Turing架構(gòu)

上圖是采納了Turing架構(gòu)的TU102 GPU,它的特點(diǎn)如下:
6 GPC(圖形處理簇)
36 TPC(紋理處理簇)
72 SM(流多處理器)
每個(gè)GPC有6個(gè)TPC,每個(gè)TPC有2個(gè)SM
4,608 CUDA核
72 RT核
576 Tensor核
288 紋理單元
12x32位 GDDR6內(nèi)存控制器 (共384位)
該架構(gòu)非常新,處理單元也大大增加,每一個(gè)GPC中都包括了6個(gè)TPC,每個(gè)TPC中包括了12個(gè)SM,每個(gè)SM中有了新的設(shè)計(jì),包括了64 CUDA核、8 Tensor核、256 KB寄存器文件,圖中標(biāo)注出一共有4個(gè)Warp調(diào)度器,可以同時(shí)運(yùn)行4個(gè)warp。(16384 * 32bit = 64KB,所以64 * 4 = 256KB的寄存器大?。?/p>
與Volta架構(gòu)類似,Turing提供了64個(gè)fp32核心,64個(gè)int32核心和8個(gè)改進(jìn)的混合精度Tensor Cores核心。這使得fp32和int32的操作可以同時(shí)執(zhí)行。但Turing僅提供2個(gè)fp64核心,因此雙精度的計(jì)算吞吐量較弱。

二、GPU邏輯執(zhí)行結(jié)構(gòu)
軟件概念:
thread-->block-->grid:在利用cuda進(jìn)行編程時(shí),一個(gè)kernel對(duì)應(yīng)一個(gè)GRID(http://blog.sina.com.cn/s/blog_80ce3a550101lntp.html),一個(gè)grid分為多個(gè)block,而一個(gè)block分為多個(gè)thread,GRID跑在GPU上的時(shí)候,可能是獨(dú)占一個(gè)GPU的,也可能是多個(gè)kernel并發(fā)占用一個(gè)GPU的(需要fermi及更新的GPU架構(gòu)支持)。其中任務(wù)劃分到是否影響最后的執(zhí)行效果。劃分的依據(jù)是任務(wù)特性和GPU本身的硬件特性。GRID,BLOCK,THREAD是軟件概念,而非硬件的概念。

上圖比較清晰的給出了三者之間的概念圖,這里需要注意的地方是,一個(gè)block中包括多個(gè)線程而這些線程會(huì)統(tǒng)一放到一個(gè)SM中的core中進(jìn)行操作,這里需要注意的地方是,一個(gè)warp為32個(gè)核(core),如果有n個(gè)Warp scheduler,那么我SM一次便只能執(zhí)行n個(gè)warp,然而一個(gè)block可以包含很多線程,那怎么并行呢?
例如我們?cè)?080Ti上測(cè)試,一個(gè)block可以包含1024個(gè)線程,即32個(gè)warp。但是如果我一次只能運(yùn)行有限(<32)個(gè)warp,那剩下的warp就要排隊(duì)(并不是真正的完全并行)。
這里可以補(bǔ)充一點(diǎn),根據(jù)https://stackoverflow.com/questions/6605581/what-is-the-context-switching-mechanism-in-gpu中的說(shuō)法,一個(gè)block會(huì)在任務(wù)開(kāi)始前將所有線程的的狀態(tài)保存到寄存器中,如果當(dāng)前寄存器不夠,那就等待(Thus a block is not launched until there are sufficient registers for all warps of the block, and until there is enough free shared memory for the block.)。所以上下文切換其實(shí)就是不斷的切換寄存器即可,所以GPU內(nèi)部的context switch非??焖伲ú恍枰馛PU中需要將寄存器的值保存到memory中)。

從這個(gè)結(jié)果里也能發(fā)現(xiàn),一個(gè)block最多有1024個(gè)線程,每個(gè)sm最多的線程也是1024。能呼應(yīng)上“一個(gè)block運(yùn)行在一個(gè)SM”這句話。
總結(jié)一下,比較清晰的地方是邏輯上的block概念對(duì)應(yīng)物理的SM,邏輯的thread對(duì)應(yīng)物理的SM中的Core。那grid這個(gè)概念我個(gè)人感覺(jué)其實(shí)沒(méi)有對(duì)應(yīng)的單位,即同一個(gè)grid中的block其實(shí)可以放到一個(gè)sm中,也可以放到多個(gè)sm中,具體是什么樣的放置方法,GPU會(huì)自行調(diào)度以達(dá)到性能最優(yōu)。
所以有個(gè)比較簡(jiǎn)單的優(yōu)化規(guī)則(這里感謝hnu黃博士的分享):

三、GPU存儲(chǔ)結(jié)構(gòu)
本章節(jié)參考https://zhuanlan.zhihu.com/p/108019839
GPU內(nèi)部結(jié)構(gòu)除了計(jì)算單元外,最重要的就是存儲(chǔ)單元。這里就將GPU中最常用的存儲(chǔ)結(jié)構(gòu)進(jìn)行一個(gè)詳細(xì)介紹,便于讀者更深入的了解GPU。
這里借用上面鏈接中的一張圖:

圖中給定了一個(gè)訪問(wèn)速度,不同存儲(chǔ)單元所需要花費(fèi)的時(shí)鐘周期信息比較清晰的給了出來(lái)。
CPU的存儲(chǔ)單元包括全局存儲(chǔ),紋理存儲(chǔ),常量存儲(chǔ),共享存儲(chǔ),局部存儲(chǔ)和寄存器等。
最右側(cè)的scope表示該存儲(chǔ)結(jié)構(gòu)在GPU內(nèi)部的作用域,例如register作用單元為一個(gè)線程,shared memory表示一個(gè)block之內(nèi)的單元進(jìn)行共用。具體的邏輯結(jié)構(gòu)圖如下:

從GPU的結(jié)構(gòu)來(lái)看,是分成Grid->Block->Thread,所以,GPU也是針對(duì)這個(gè)層次來(lái)設(shè)計(jì)存儲(chǔ)結(jié)構(gòu)的,從上面的表格中可以看到。register和local memory是針對(duì)Thread來(lái)設(shè)計(jì)的,shared memory是針對(duì)Block來(lái)設(shè)計(jì),而其他的三個(gè)是針對(duì)Grid來(lái)進(jìn)行設(shè)計(jì)的。
而Constant Memory,Texture Memory是用來(lái)加速GPU運(yùn)行而設(shè)計(jì)的。
下面我們從小到大分析一下各個(gè)存儲(chǔ)結(jié)構(gòu):
1 寄存器(register)
寄存器是速度最快的存儲(chǔ)單元,位于GPU芯片的SM上,用于存儲(chǔ)局部變量。每個(gè)SM(SMX)上有成千上萬(wàn)(65536)的32位寄存器,當(dāng)kernel函數(shù)啟動(dòng)后,這些寄存器被分配給指定的線程來(lái)使用。由于不同kernel函數(shù)需要的寄存器數(shù)量也不相等,所以,也有一個(gè)規(guī)定一個(gè)線程的最大寄存器數(shù)量是256個(gè)。
2 Local Memory
Local Memory本身在硬件中沒(méi)有特定的存儲(chǔ)單元,而是從Global Memory虛擬出來(lái)的地址空間。Local Memory是為寄存器無(wú)法滿足存儲(chǔ)需求的情況而設(shè)計(jì)的,主要是用于存放單線程的大型數(shù)組和變量。Local Memory是線程私有的,線程之間是不可見(jiàn)的。由于GPU硬件單位沒(méi)有Local Memory的存儲(chǔ)單元,所以,針對(duì)它的訪問(wèn)是比較慢的。從上面的表格中,也可以看到跟Global Memory的訪問(wèn)速度是接近的。

3 Shared Memory
Shared Memory位于GPU芯片上,訪問(wèn)延遲僅次于寄存器。Shared Memory是可以被一個(gè)Block中的所有Thread來(lái)進(jìn)行訪問(wèn)的,可以實(shí)現(xiàn)Block內(nèi)的線程間的低開(kāi)銷(xiāo)通信。在SMX中,L1 Cache跟Shared Memory是共享一個(gè)64KB的存儲(chǔ)單元的,他們之間的大小劃分不同的GPU結(jié)構(gòu)不一樣;

4 Constant Memory
Constant Memory類似于Local Memory,也是沒(méi)有特定的存儲(chǔ)單元的,只是Global Memory的虛擬地址。因?yàn)樗侵蛔x的,所以簡(jiǎn)化了緩存管理,硬件無(wú)需管理復(fù)雜的回寫(xiě)策略。Constant Memory啟動(dòng)的條件是同一個(gè)warp所有的線程同時(shí)訪問(wèn)同樣的常量數(shù)據(jù)。
5 Texture Memory
Texture Memory是GPU的重要特性之一,也是GPU編程優(yōu)化的關(guān)鍵。Texture Memory實(shí)際上也是Global Memory的一部分,但是它有自己專用的只讀cache。這個(gè)cache在浮點(diǎn)運(yùn)算很有用,Texture Memory是針對(duì)2D空間局部性的優(yōu)化策略,所以thread要獲取2D數(shù)據(jù)就可以使用texture Memory來(lái)達(dá)到很高的性能。從讀取性能的角度跟Constant Memory類似。
6 Global Memory
Global Memory是GPU中最大的存儲(chǔ)單元,Host memory與GPU之間的數(shù)據(jù)交互均會(huì)通過(guò)Global Memory進(jìn)行保存,它也是讀取速度最慢的組件。GPU中所有計(jì)算單元均可以訪問(wèn)該存儲(chǔ)單元。

https://nyu-cds.github.io/python-gpu/02-cuda/