1. GPU Resource Management
GPU channel是GPU與CPU之間的橋接接口邑飒,通過(guò)CPU向GPU發(fā)送GPU指令的唯一通道循签,GPU channel包含了兩類(lèi)用于存儲(chǔ)GPU指令的buffer:
- GPU command buffer (也稱之為FIFO push buffer)
- Ring buffer (也稱之為indirect buffer),從上圖中看出疙咸,這個(gè)buffer是環(huán)形結(jié)構(gòu)的县匠,即其容量是固定的,這也是為什么叫Ring buffer的原因吧
當(dāng)GPU指令被寫(xiě)入到GPU command buffer時(shí)罕扎,系統(tǒng)還會(huì)向Ring buffer中寫(xiě)入與此指令所對(duì)應(yīng)的packet聚唐,packet包含了此指令在GPU command buffer中的偏移位置與長(zhǎng)度數(shù)據(jù)。
在執(zhí)行指令的時(shí)候腔召,GPU不是直接從GPU command buffer中讀取數(shù)據(jù)杆查,而是先經(jīng)過(guò)Ring buffer讀取出當(dāng)前待處理指令的相關(guān)信息,再據(jù)此讀取GPU command(這也是為什么Ring buffer被稱之為indirect buffer的原因)臀蛛。
2. Warp/Wavefront
現(xiàn)代GPU為了加強(qiáng)數(shù)據(jù)的并行化處理的強(qiáng)度亲桦,使用的是SIMT(Single Instruction Multi Thread,SIMD的更高級(jí)版本)體系結(jié)構(gòu)浊仆,shader program運(yùn)行的最小單位是thread客峭,多個(gè)運(yùn)行相同shader的threads會(huì)被打包到一個(gè)組(thread group),這個(gè)thread group抡柿,在NVIDIA被稱之為warp舔琅,在AMD中被稱之為wavefront。
3. NVidia Turing GPU架構(gòu)
上面這張圖是從標(biāo)題鏈接給出的Turing白皮書(shū)中截取的GPU架構(gòu)圖洲劣,其中包含如下幾個(gè)關(guān)鍵縮寫(xiě):
- GPC(Graphics Processor Cluster):每個(gè)GPU會(huì)包含多個(gè)GPCs备蚓,每個(gè)GPC對(duì)應(yīng)于一個(gè)獨(dú)有的Raster Engine,多個(gè)TPC(Texture Processor Cluster)囱稽。
- TPC(Texture Processor Cluster):每個(gè)TPC對(duì)應(yīng)于一個(gè)PolyMorph Engine(負(fù)責(zé)vertex fetching, viewport transform, 以及tessellation等工作)郊尝,以及多個(gè)(圖中是兩個(gè))SM(Streaming Multiprocessor)。
- SM(Streaming Multiprocessor):SM是GPU中負(fù)責(zé)完成實(shí)際的計(jì)算過(guò)程的功能單元战惊,是一種通用的處理器組件(即可以用于VS/GS/PS等的計(jì)算流昏,這是從Tesla架構(gòu)就開(kāi)啟的特性,可以用于平衡多種Shader之間的計(jì)算消耗,避免了專屬Processor的浪費(fèi))况凉,與CPU不同谚鄙,SM支持指令級(jí)別的并行處理,但是卻不支持各線程獨(dú)立的branch/loop操作刁绒。如下圖所示襟锐,每個(gè)都有自己獨(dú)有的控制單元(Warp Scheduler,Dispatch等)膛锭,寄存器組件,執(zhí)行管線以及共享緩存等蚊荣。每個(gè)SM被分成多個(gè)處理塊(Processing Block)初狰,每個(gè)Block中包含一個(gè)獨(dú)立的Warp Scheduler,一個(gè)獨(dú)立的Dispatch Unit互例,一個(gè)一定大小的Register File奢入,一個(gè)獨(dú)立的L0 Instruction Cache(圖中未展示),多個(gè)INT32處理單元媳叨,多個(gè)FP32處理單元腥光,以及多個(gè)TENSOR CORES單元。
- Warp Scheduler:實(shí)現(xiàn)Warp的快速切換糊秆,包括上下文切換武福,運(yùn)行指令的切換等。每個(gè)SM包含了多個(gè)Warp(線程組)痘番,每個(gè)線程處理一個(gè)Vertex/Pixel捉片,每個(gè)Warp當(dāng)前執(zhí)行的指令通過(guò)Warp Scheduler來(lái)指定,Warp中的所有線程按照l(shuí)ock-step方式執(zhí)行汞舱,即所有線程同一時(shí)刻執(zhí)行的是完全相同的指令伍纫。當(dāng)某個(gè)Warp處于阻塞狀態(tài)(比如I/O或者Texture RW等)時(shí),此時(shí)會(huì)通過(guò)Scheduler完成Warp之間的切換昂芜,避免GPU浪費(fèi)莹规。每個(gè)Warp都需要用到一定的寄存器來(lái)進(jìn)行數(shù)據(jù)的存儲(chǔ),當(dāng)Warp所需的寄存器數(shù)目較多泌神,在SM中總共的寄存器數(shù)目固定的情況下良漱,SM所能同時(shí)支持的Warp的數(shù)目就會(huì)受到限制。
- Dispatch Unit:Instruction派發(fā)單元
- Registers File(簡(jiǎn)稱RF):每個(gè)SM包含了數(shù)以千計(jì)的寄存器單元
- INT32:整數(shù)處理單元(占比36%腻扇,根據(jù)NVidia統(tǒng)計(jì)债热,shader運(yùn)算中大概三成是整型計(jì)算,這個(gè)占比有助于提升GPU利用率)幼苛,在此前的Pascal GPU架構(gòu)中窒篱,整數(shù)計(jì)算與浮點(diǎn)數(shù)運(yùn)算使用的是相同的數(shù)據(jù)處理路徑,在進(jìn)行整數(shù)處理的時(shí)候,會(huì)阻塞浮點(diǎn)數(shù)操作的執(zhí)行墙杯,而在Turing架構(gòu)中增加了整數(shù)處理路徑配并,允許浮點(diǎn)數(shù)與整數(shù)同步進(jìn)行處理,有助于提高GPU的利用率高镐。
- FP32:浮點(diǎn)數(shù)(單精度)處理單元(其實(shí)現(xiàn)在已經(jīng)有了雙精度浮點(diǎn)數(shù)處理單元溉旋,只是圖中未列出)
- TENSOR CORES:為tensor/matrix計(jì)算所專門(mén)設(shè)計(jì)的處理器核心,這類(lèi)計(jì)算在deep learning中使用頻率較高嫉髓,可以用于支持Deep Learning Super Sampling(DLSS)抗鋸齒技術(shù)(使用深度學(xué)習(xí)的算法從多幀中提取關(guān)鍵信息來(lái)補(bǔ)足鋸齒處的采樣精度观腊,其采樣點(diǎn)數(shù)目要少于傳統(tǒng)的抗鋸齒技術(shù)比如TAA,同時(shí)還可以避免透明物體處理時(shí)的復(fù)雜邏輯)算行。
- SFU:Special Function單元梧油,用于實(shí)現(xiàn)一些特殊函數(shù)如三角函數(shù),指數(shù)函數(shù)等的計(jì)算(單精度州邢?)儡陨。此單元可以與Dispatch Unit解耦,即在此單元處于工作狀態(tài)時(shí)量淌,Dispatch Unit依然可以執(zhí)行其他的運(yùn)算指令的分派
- LD/ST:Load/Store單元骗村,負(fù)責(zé)數(shù)據(jù)的讀寫(xiě)邏輯
- TEX:用作貼圖訪問(wèn)(SRV/UAV等)。
- L1 Data Cache / Shared Memory:所有Processor共享呀枢,降低Local/Global Memory讀取的延遲
- RT Core:基于硬件的Ray Tracing加速組件胚股。將此組件與一個(gè)降噪算法相結(jié)合,可以降低單個(gè)pixel所需要的射線數(shù)目硫狞。
4. GPU存儲(chǔ)結(jié)構(gòu)
GPU中用于存儲(chǔ)數(shù)據(jù)的結(jié)構(gòu)有多種[4]信轿,分別是:
- Local Memory(LMEM)
- Global Memory(GMEM)
- Texture Memory(TMEM)
- Constant Memory(CMEM)
- Register Memory(RMEM)
- Shared Memory(SMEM)
每種存儲(chǔ)結(jié)構(gòu)都有著各自的優(yōu)缺點(diǎn),因此適用于不同的應(yīng)用場(chǎng)景残吩,從訪問(wèn)速度來(lái)看财忽,這些存儲(chǔ)結(jié)構(gòu)按照從高到低排序依次是:
RMEM > SMEM > CMEM > TMEM > LMEM > GMEM
RMEM與SMEM是直接集成在GPU芯片上的,而剩下的幾種存儲(chǔ)結(jié)構(gòu)則是在GPU之外的芯片上的泣侮,此外即彪,LMEM/CMEM/TMEM都有著各自的緩存機(jī)制,即在訪問(wèn)數(shù)據(jù)的時(shí)候都會(huì)首先從緩存中進(jìn)行查找判斷活尊,再?zèng)Q定是否需要從更低一級(jí)速度的存儲(chǔ)結(jié)構(gòu)中進(jìn)行讀取隶校。
4.1 Local Memory
存儲(chǔ)在LMEM中的數(shù)據(jù)可見(jiàn)性與RMEM一樣,都是只對(duì)負(fù)責(zé)對(duì)其進(jìn)行讀寫(xiě)的線程可見(jiàn)蛹锰。LMEM實(shí)際上并不是一塊物理存儲(chǔ)空間深胳,而是對(duì)GMEM的一個(gè)抽象,因此其訪問(wèn)速度與對(duì)GMEM的訪問(wèn)速度是相同的铜犬。LMEM中的數(shù)據(jù)對(duì)于一個(gè)線程而言是Local的(即只從屬于當(dāng)前thread的空間舞终,對(duì)其他線程不可見(jiàn))轻庆,通常用于存儲(chǔ)一些automatic變量(automatic變量指的是一些大尺寸的數(shù)據(jù)結(jié)構(gòu)或者數(shù)組,因?yàn)榧拇嫫鞑粔蛄踩埃虼藭?huì)塞入LMEM中)余爆,編譯器在寄存器不足的時(shí)候,就會(huì)從GMEM中開(kāi)辟一塊空間用作LMEM夸盟。
雖然LMEM是從GMEM中分割出來(lái)的蛾方,但是其使用方式與GMEM還是有著一些區(qū)別:
- 對(duì)LMEM中數(shù)據(jù)的尋址是由編譯器(compiler)完成的
- 對(duì)LEME中的數(shù)據(jù)的讀寫(xiě)會(huì)有一個(gè)緩存機(jī)制
如上圖所示(從圖中可以看出,L1是位于GPU芯片上的上陕,其中SMEM就存儲(chǔ)在其中桩砰,RMEM也是在芯片上,而L2及以后的存儲(chǔ)空間則都是芯片之外的存儲(chǔ)空間了)释簿,在對(duì)LMEM進(jìn)行數(shù)據(jù)讀寫(xiě)的時(shí)候五芝,會(huì)經(jīng)歷這樣一個(gè)緩存層級(jí)流動(dòng):L1->L2->LMEM。因?yàn)長(zhǎng)MEM實(shí)際上是臨時(shí)開(kāi)辟的一塊空間辕万,因此里面的數(shù)據(jù)實(shí)際上是GPU先寫(xiě)入的,在此之前發(fā)生的讀取就相當(dāng)于讀到了一堆亂碼沉删。
那么什么情況下會(huì)使用到LMEM呢渐尿?一般來(lái)說(shuō)有如下兩種情形:
- 每個(gè)線程所需要的寄存器超出硬件支持的限度,出現(xiàn)spilling
- 每個(gè)線程需要分配一塊連續(xù)空間進(jìn)行數(shù)組數(shù)據(jù)的存儲(chǔ)矾瑰,在一些不支持索引操作(寄存器不可索引)
的編譯器上砖茸,就需要采用LMEM實(shí)現(xiàn)數(shù)組數(shù)據(jù)存儲(chǔ)
因?yàn)長(zhǎng)MEM相對(duì)于寄存器訪問(wèn)速度的低效性,因此其對(duì)性能的影響主要有如下兩個(gè)方面:
- 增加內(nèi)存訪問(wèn)的沖突程度(增加了對(duì)GMEM的訪問(wèn)殴穴,對(duì)帶寬消耗增加)
- 增加shader指令數(shù)
但是因?yàn)橐韵碌膬牲c(diǎn)原因凉夯,LMEM也不一定會(huì)造成性能下降:
- 由于LMEM支持緩存,因此通過(guò)這種方式可以減輕內(nèi)存訪問(wèn)的沖突程度
- 如果shader并不受指令吞吐量的限制(即指令的吞吐量并非瓶頸)的話采幌,LMEM的啟用并不會(huì)對(duì)性能造成實(shí)質(zhì)性影響
對(duì)于一些LMEM可能會(huì)存在瓶頸的情況劲够,參考文獻(xiàn)[3]中給出了一些分析的方法可供排查,同時(shí)還給出了對(duì)應(yīng)的優(yōu)化策略以及實(shí)戰(zhàn)案例休傍,有興趣的同學(xué)可以前往參考征绎。
4.2 Register Memory
存儲(chǔ)在RMEM中的數(shù)據(jù)只對(duì)負(fù)責(zé)對(duì)此寄存器進(jìn)行讀寫(xiě)的線程可見(jiàn),且其生命周期與此線程的生命周期一致磨取。
通常情況下人柿,對(duì)寄存器的訪問(wèn)不需要消耗時(shí)鐘周期,但是在一些特殊情況(比如先進(jìn)行了一個(gè)寫(xiě)操作忙厌,之后再進(jìn)行讀取凫岖,或者在bank訪問(wèn)沖突的情況下),會(huì)有例外逢净。先寫(xiě)后讀的延遲大概是24個(gè)時(shí)鐘周期哥放,對(duì)于更新的GPU(每個(gè)SM包含32個(gè)cores的情況)歼指,可能需要花費(fèi)768個(gè)線程來(lái)隱藏這個(gè)延遲。
當(dāng)需求的寄存器數(shù)目超出硬件所能支持的限額時(shí)婶芭,就會(huì)導(dǎo)致寄存器壓力东臀,在這種情況下,數(shù)據(jù)就會(huì)使用LMEM來(lái)進(jìn)行存儲(chǔ)(所謂的spilled over犀农,即溢出)惰赋,如下圖所示[3]:
4.3 Shared Memory
存儲(chǔ)在SMEM中的數(shù)據(jù)對(duì)處于同一個(gè)block所有的線程都是可見(jiàn)的(不負(fù)shared之名),因此通常用于多個(gè)線程之間的數(shù)據(jù)互換呵哨,為了避免多個(gè)線程同時(shí)訪問(wèn)相同的數(shù)據(jù)導(dǎo)致的阻塞赁濒,NVIDIA將SMEM劃分成32個(gè)邏輯單元,每個(gè)單元叫做一個(gè)bank孟害,在內(nèi)存中連續(xù)的數(shù)據(jù)拒炎,在banks的分布也是連續(xù)的:
SMEM是位于L1 Cache中的,其尺寸通常為16/32/48KB挨务,剩余部分用作L1 Cache击你,對(duì)于開(kāi)普勒架構(gòu)而言,每個(gè)bank每個(gè)時(shí)鐘的帶寬是64bits/clock谎柄,較早的Fermi架構(gòu)時(shí)鐘不太一樣丁侄,但是帶寬差不多是這個(gè)數(shù)值的一半。
由于一個(gè)warp中有32個(gè)線程朝巫,因此總共需要32個(gè)SMEM banks鸿摇。由于每個(gè)bank在每個(gè)時(shí)鐘周期中只支持一次訪問(wèn)請(qǐng)求,因此多個(gè)同時(shí)訪問(wèn)的請(qǐng)求就會(huì)導(dǎo)致bank conflict劈猿,這個(gè)的處理過(guò)程后面會(huì)講拙吉。
默認(rèn)每個(gè)bank占用32bits(4bytes),開(kāi)普勒架構(gòu)之后揪荣,可以通過(guò)指令(cudaDeviceSetSharedMemConfig(cudaSharedMemBankSizeEightByte))將每個(gè)bank擴(kuò)充到64bits筷黔,以應(yīng)對(duì)雙精度數(shù)據(jù)的訪問(wèn)沖突。
4.4 Global Memory
存儲(chǔ)在Global Memory中的數(shù)據(jù)對(duì)于當(dāng)前進(jìn)程中的所有線程都是可見(jiàn)的仗颈,其生命周期與進(jìn)程一致必逆。
4.5 Constant Memory
CMEM通常用于存儲(chǔ)一些常量數(shù)據(jù),當(dāng)同一個(gè)warp中的所有線程都需要使用同一個(gè)參數(shù)時(shí)揽乱,可以將數(shù)據(jù)放在CMEM中名眉,這種做法比將數(shù)據(jù)放在GMEM中更節(jié)省帶寬。
4.6 Texture Memory
TMEM也是一種常量存儲(chǔ)結(jié)構(gòu)凰棉,當(dāng)一個(gè)warp中的線程所需要讀取的數(shù)據(jù)都是存儲(chǔ)位置上相鄰的時(shí)候损拢,使用這種結(jié)構(gòu)比GMEM具有更優(yōu)的性能表現(xiàn)(也是出于帶寬的原因)
參考文獻(xiàn)
[1]. A HISTORY OF NVIDIA STREAM MULTIPROCESSOR
[2]. Life of a triangle - NVIDIA's logical pipeline
[3]. Local Memory and Register Spilling
[4]. GPU Memory Types – Performance Comparison