预处理器命令必须作为第一个非空白空间启动_第三章 图形处理器(上)
--Jen-Hsun Huang
歷史上,圖形加速始于在與三角形重疊的每個(gè)像素掃描線上對(duì)顏色插值,然后顯示這些值。包括訪問(wèn)圖像數(shù)據(jù)將紋理應(yīng)用于表面的能力。為插值和z深度測(cè)試添加硬件提供內(nèi)置的可見(jiàn)性檢測(cè)。因?yàn)樗鼈兘?jīng)常使用,這些處理被提交到專門的硬件用以提升性能。渲染管道的更多部分,每部分的更多功能,被加到后續(xù)的硬件中。專用圖像硬件相對(duì)CPU的唯一計(jì)算優(yōu)勢(shì)是速度,但是速度很關(guān)鍵。
在過(guò)去的二十年中,圖形硬件經(jīng)歷了不可思議的轉(zhuǎn)變。第一款包含硬件頂點(diǎn)處理的消??費(fèi)類圖形芯片(NVIDIA的GeForce256)于1999年發(fā)售。NVIDIA創(chuàng)造了術(shù)語(yǔ)圖形處理單元(GPU),以區(qū)分GeForce 256和以前可用的光柵化芯片,并且沿用下來(lái)。在接下來(lái)的幾年中,GPU從可配置復(fù)雜的固定功能流水線到高度可編程的空白石板(blank slates)演化,開(kāi)發(fā)人員可以在那里實(shí)現(xiàn)自己的算法。不同種類的可編程著色器是控制GPU的主要手段。為了提高效率,管道的某些部分仍然是可配置的,不是可編程的,但是趨勢(shì)是可編程性和靈活性。
GPU通過(guò)專注于一組(a narrow set of)可高度并行化的任務(wù)從而獲得出色的速度。他們擁有專用于實(shí)現(xiàn)z緩沖區(qū)的定制芯片,可以快速訪問(wèn)紋理圖像和其他緩沖區(qū),例如,找到一個(gè)三角形所覆蓋的那些像素。這些元素如何執(zhí)行這些功能在23章中介紹。早期更重要的是要知道GPU如何為它的可編程著色器完成并行化。
3.3節(jié)解釋了著色器的功能(how shaders function)。現(xiàn)在,你需要知道的是著色器的核心是一個(gè)小的處理器,用來(lái)做一些相對(duì)孤立的任務(wù),比如把一個(gè)頂點(diǎn)從世界左邊變換為屏幕坐標(biāo),或者是計(jì)算一個(gè)被三角形覆蓋的像素的顏色。隨著每幀數(shù)千或數(shù)百萬(wàn)三角形被發(fā)送到屏幕上,每秒有數(shù)十億次的著色器調(diào)用,也就是說(shuō),著色器程序運(yùn)行在單獨(dú)的實(shí)例上。
首先,延遲是所有處理器都面臨的問(wèn)題。訪問(wèn)數(shù)據(jù)需要一些時(shí)間。考慮延遲的一個(gè)基本方法是,信息離處理器越遠(yuǎn),就要等待越長(zhǎng)時(shí)間。23.3節(jié)介紹延遲的更多細(xì)節(jié)。訪問(wèn)存儲(chǔ)在內(nèi)存芯片中的信息比訪問(wèn)本地寄存器信息需要花費(fèi)更長(zhǎng)時(shí)間。18.4.1節(jié)討論內(nèi)存訪問(wèn)的更多細(xì)節(jié)。關(guān)鍵點(diǎn)是等待獲取數(shù)據(jù)意味著處理器處理器會(huì)被拖延(stalled),這會(huì)降低性能。
3.1 數(shù)據(jù)并行架構(gòu)
不同的處理器框架采用不同的策略避免拖延(stalls)。CPU已針對(duì)各種數(shù)據(jù)結(jié)構(gòu)和大型代碼庫(kù)進(jìn)行了優(yōu)化。CPU可以有多個(gè)處理器,但是每個(gè)處理器都以串行方式運(yùn)行代碼,SIMD向量處理是次要的例外。為了盡量減少延遲的影響,CPU的大部分芯片都由快速本地緩存組成,本地緩沖被接下來(lái)可能需要的數(shù)據(jù)所填充。CPU還通過(guò)使用諸如分支預(yù)判(branch prediction),指令重排(instruction reordering),寄存器重命名(register renaming),緩存預(yù)取(cache prefetching)等精巧技術(shù)來(lái)避免拖延(stalls)。
GPU采用不同的方法。GPU大部分芯片區(qū)域?qū)S糜诖罅康奶幚砥?#xff0c;叫做著色器核心,通常有數(shù)千個(gè)。GPU是流處理器,其中依次處理相似數(shù)據(jù)的有序集。因?yàn)檫@種相似性(例如一組頂點(diǎn)或像素),GPU可以用大規(guī)模并行的方式來(lái)處理這些數(shù)據(jù)。另一個(gè)重要因素是這些調(diào)用盡可能獨(dú)立,這樣它們不需要鄰近調(diào)用的信息并且不共享可寫(xiě)的內(nèi)存位置。這個(gè)規(guī)則有時(shí)會(huì)被破壞以允許新的和有用的功能,但是這樣的例外會(huì)以潛在的延遲作為代價(jià),因?yàn)橐粋€(gè)處理器獲取會(huì)等待另一個(gè)處理器完成它的工作。
GPU針對(duì)吞吐量進(jìn)行了優(yōu)化,吞吐量被定義為數(shù)據(jù)的最大處理速率。然而,這種快速處理具有成本。更少的芯片區(qū)域?qū)S糜诰彺婧涂刂七壿?#xff0c;每個(gè)著色器核心的延遲通常是大大高于CPU處理器遇到的情況。
假設(shè)一個(gè)網(wǎng)格物體已被光柵化,有兩千個(gè)像素有要處理的片段(fragments);一個(gè)像素著色程序要被調(diào)用兩千次。想象這里只有一個(gè)著色器處理器,世界上最弱的GPU。它開(kāi)始為兩千個(gè)片段的第一個(gè)執(zhí)行著色程序。著色器處理器對(duì)寄存器中的值執(zhí)行一些算數(shù)運(yùn)算。寄存器是本地的訪問(wèn)很快,沒(méi)有拖延(stall)發(fā)生。著色器處理器接著執(zhí)行,例如執(zhí)行到一個(gè)紋理訪問(wèn);例如,對(duì)于一個(gè)給定的表面位置,程序需要知道應(yīng)用到網(wǎng)格上的圖片的像素顏色。紋理是一個(gè)完全獨(dú)立的資源,不是像素程序本地內(nèi)存的一部分,紋理訪問(wèn)參與進(jìn)來(lái)。一次內(nèi)存獲取可以花費(fèi)數(shù)百至數(shù)千個(gè)時(shí)鐘周期,在那期間GPU處理器什么都不做。此時(shí),著色器處理器會(huì)拖延(stall),等待紋理的顏色值返回。
為了使這個(gè)GPU有明顯的改觀,給每個(gè)前段的本地寄存器一點(diǎn)存儲(chǔ)空間。現(xiàn)在,著色器處理器允許切換并執(zhí)行另一個(gè)片段(兩千個(gè)中的第二個(gè)),而不是拖延(stalling)在紋理獲取。這個(gè)切換非常快,第一個(gè)和第二個(gè)片段什么都沒(méi)受到影響,除了第一個(gè)片段上沒(méi)有指令再執(zhí)行。現(xiàn)在第二個(gè)片段被執(zhí)行。和執(zhí)行第一個(gè)時(shí)一樣,一些算數(shù)函數(shù)被執(zhí)行,然后再次遇到紋理獲取。著色器核心現(xiàn)在切換到另一個(gè)片段,第三個(gè)。最終兩三個(gè)片段全部以這種方式被執(zhí)行。這時(shí)著色器處理器返回到第一個(gè)片段。這次紋理顏色被獲取到并且可以使用,因此著色器程序可以繼續(xù)執(zhí)行。處理器用同樣的方式處理直到另一個(gè)拖延(stall)執(zhí)行的指令被遇到,或者程序完成。一個(gè)片段會(huì)話費(fèi)更長(zhǎng)的執(zhí)行時(shí)間相比于著色器處理器專注于一個(gè)片段,但是所有片段的總執(zhí)行時(shí)間大大的減少了。
在這個(gè)架構(gòu)中,通過(guò)切換到另一個(gè)片段讓GPU保持忙碌來(lái)隱藏延遲。GPU通過(guò)分離指令執(zhí)行邏輯和數(shù)據(jù)來(lái)使這個(gè)設(shè)計(jì)更進(jìn)一步。叫做單指令,多數(shù)據(jù)(SIMD),這個(gè)安排以固定的步數(shù)(lock-step)在固定數(shù)量的著色器程序上執(zhí)行相同的命令。SIMD的優(yōu)勢(shì)是專用于處理數(shù)據(jù)和切換的硅(和電量)明顯減少,相比于使用獨(dú)立的邏輯和分配單元去運(yùn)行每個(gè)程序。把我們兩千個(gè)片段的例子轉(zhuǎn)換成現(xiàn)代GPU術(shù)語(yǔ),一個(gè)片段的每個(gè)像素著色器調(diào)用叫做一個(gè)線程。這個(gè)類型的線程不同于CPU線程。它由著色器輸入值的一點(diǎn)內(nèi)存,和著色器執(zhí)行所需的任意寄存器空間組成。使用相同著色器程序的線程被打包成組,在NVIDIA中叫warps,AMD叫wavefronts。一個(gè)warps或wavefronts通過(guò)一些數(shù)量的GPU著色器核心被安排執(zhí)行,從8到64的任何地方,使用SIMD處理。每個(gè)線程被映射到一個(gè)SIMD通道(lane)。
假設(shè)我們有兩千個(gè)線程要執(zhí)行。在NVIDIA GPU的Warps上包含32個(gè)線程。這產(chǎn)生了2000/32=62.5個(gè)warps,意味著分配了63個(gè)warps,一個(gè)warp一半是空的。warp的執(zhí)行和我們單個(gè)GPU處理器的例子類似。著色器程序在所有32個(gè)處理器上以固定步數(shù)(lock-step)執(zhí)行。當(dāng)遇到內(nèi)存獲取,所有線程同時(shí)遇到,因?yàn)橥瑯拥闹噶钤谒械木€程上執(zhí)行。獲取示意這個(gè)warp的線程將拖延(stall),所有都在等待它們的(不同的)結(jié)果。這里不會(huì)拖延(stall),這個(gè)warp會(huì)為一個(gè)不同的32個(gè)線程的warp交換出去,換進(jìn)來(lái)的warp然后被32個(gè)核心執(zhí)行。這個(gè)交換和我們單一處理器系統(tǒng)一樣快,因此當(dāng)一個(gè)warp被換進(jìn)或換出時(shí),每個(gè)線程沒(méi)有數(shù)據(jù)被觸及(touched)。每個(gè)線程有它自己的寄存器,每個(gè)warp跟蹤正在執(zhí)行那條指令。換進(jìn)一個(gè)新的warp只是將一組核心指向不同的一組線程去執(zhí)行;這里沒(méi)有其它開(kāi)銷。Warps執(zhí)行或者換出知道所有都完成。看圖3.1。
著我們這個(gè)簡(jiǎn)單的例子中一張紋理的內(nèi)存獲取延遲能導(dǎo)致warp交換出去。在實(shí)際中warps會(huì)因?yàn)楦痰难舆t被交換出去,因?yàn)榻粨Q的成本非常低。這里還有一些其它的技術(shù)用來(lái)優(yōu)化執(zhí)行,但是warp-swapping是所有GPU使用的主要的延遲隱藏(latency-hiding)機(jī)制。此過(guò)程的效率如何涉及多個(gè)因素。例如,如果這里只有少量的線程,然后只有少量的warps會(huì)被創(chuàng)建,從而導(dǎo)致延遲隱藏(latency hiding)有問(wèn)題。
著色器程序的結(jié)構(gòu)是影響效率的一個(gè)重要特征。一個(gè)主要因素是每個(gè)線程使用的寄存器數(shù)量。在我們的例子中我們假設(shè)可以一次將2000個(gè)線程全部駐留在GPU上。每個(gè)線程關(guān)聯(lián)的著色器程序所需的寄存器越多,能駐留在GPU中的線程就更少,因此warps也更少。warps的短缺可能意味著掛起(stall)不能通過(guò)交換減輕。駐留的warps被叫做“飛行中”,駐留的數(shù)量叫做占用(occupancy)。高占用意味著這兒有許多可用與處理的warps,因此空閑處理器可能更少。占用率低通常會(huì)導(dǎo)致性能不佳。內(nèi)存獲取的頻率同樣影響需要多少延遲隱藏(latency hiding)。Lauritzen概述了占用被寄存器的數(shù)量和一個(gè)著色器使用共享內(nèi)存的影響。Wronski討論了理想的占用率如何根據(jù)一個(gè)著色器執(zhí)行的操作類型而變化。
影響整體效率的另一個(gè)因素是動(dòng)態(tài)分支,由"if"語(yǔ)句和循環(huán)造成。加入在一個(gè)著色器程序中遇到一條“if”語(yǔ)句,如果所有的線程求值采用相同的分支,warp可以繼續(xù)而不用考慮其它分支。然而,如果一些線程,或者甚至只是一個(gè)線程,采用另一條路徑,那么warp必須執(zhí)行這兩個(gè)分支,扔掉每個(gè)特定線程不需要的結(jié)果。這個(gè)問(wèn)題叫做線程分歧(thread divergence),其中一些線程可能需要執(zhí)行一個(gè)循環(huán)迭代或者執(zhí)行一個(gè)"if"路徑,而其它warp內(nèi)的線程不需要,在此期間只能讓這些不需要的線程處于空閑狀態(tài)。
所有的GPU實(shí)現(xiàn)這種架構(gòu)想法,導(dǎo)致系統(tǒng)有嚴(yán)格的限制但是每瓦特有大量的算力。明白系統(tǒng)如何運(yùn)轉(zhuǎn)(operates)將會(huì)幫助作為程序員的你更高效的使用它提供的算力。在后面的小節(jié)里我們討論GPU如何實(shí)現(xiàn)渲染管道,可編程著色器如何運(yùn)轉(zhuǎn)(operate),和GPU每個(gè)階段的演化和功能。
3.2 GPU管道概述
GPU實(shí)現(xiàn)概念的幾何處理,光柵化,和像素處理管道階段在第2章中描述。它們被分成幾個(gè)硬件階段有不同程度的可配置性和可編程性。圖3.2顯示了不同階段顏色根據(jù)它們可編程和可配置的程度。注意這些物理階段的拆分和第2章中介紹的功能階段有點(diǎn)不一樣。
我們這里描述GPU的邏輯模型,通過(guò)一組API暴露給程序員。就像18和23章中討論的,這個(gè)邏輯管道的實(shí)現(xiàn),物理模型,依賴于硬件供應(yīng)商。一個(gè)在邏輯模型中的固定功能階段或許通過(guò)在相鄰的可編程階段添加命令在GPU上執(zhí)行。在管道中的一個(gè)程序或許會(huì)被拆分成多個(gè)元素被獨(dú)立的子單元(sub-units)執(zhí)行,或者完全由一個(gè)單獨(dú)的通道(pass)執(zhí)行。邏輯模型可以幫助您推斷會(huì)影響性能的原因,但是它應(yīng)該不要誤解為GPU實(shí)際實(shí)現(xiàn)管道的方式。
頂點(diǎn)著色器時(shí)一個(gè)完全可編程的階段,用來(lái)實(shí)現(xiàn)幾何處理階段。幾何著色器是一個(gè)完全可編程的階段,在圖元(點(diǎn),線,和三角形)的頂點(diǎn)上操作。它可以用來(lái)執(zhí)行每圖元著色操作,銷毀圖元,或者創(chuàng)建新的圖元。曲面細(xì)分(tessellation)階段和幾何著色階段都是可選的,不是所有的GPU都支持它們,尤其是在移動(dòng)設(shè)備上。
裁剪,三角形設(shè)置(triangle setup),和三角形遍歷(triangle traversal)通過(guò)固定功能硬件實(shí)現(xiàn)。屏幕映射受窗口和視口設(shè)置的影響,內(nèi)部形成了一個(gè)簡(jiǎn)單的縮放和重新定位。像素著色器階段是完全可編程的。盡管合并(merger)階段不是可編程的,它是高度可配置的并且可以設(shè)置執(zhí)行許多操作。它實(shí)現(xiàn)"合并"(merging)的功能階段,負(fù)責(zé)修改顏色,z-buffer,混合,模板和其它輸出相關(guān)的緩沖區(qū)。像素著色器的執(zhí)行與合并階段形成了概念的像素處理階段在第2章介紹。
隨著時(shí)間的流逝,GPU管道已從硬編碼操作演變?yōu)樵鰪?qiáng)靈活性和控制力。可編程著色器階段的介紹是這一演變過(guò)程中最重要的一步。下一節(jié)將介紹各個(gè)可編程階段共有的功能。
3.3 可編程著色器階段
現(xiàn)代著色器程序使用統(tǒng)一的著色器設(shè)計(jì)。這意味著頂點(diǎn),像素,幾何,和曲面細(xì)分相關(guān)的著色器共享一個(gè)通用的編程模型。在內(nèi)部它們有相同的指令集架構(gòu)(ISA)。實(shí)現(xiàn)此模型的處理器在DirectX中叫做通用著色器核心,有這種核心的GPU被稱為有統(tǒng)一著色器架構(gòu)。這種類型的架構(gòu)背后的想法是著色器處理器可用于多種角色,GPU可以根據(jù)需要來(lái)分配這些。例如,一組由小三角形組成的網(wǎng)格比由兩個(gè)三角形組成的大正方形需要更多的頂點(diǎn)著色器處理。一種具有單獨(dú)的頂點(diǎn)和像素著色器核心池的GPU意味著為了讓所有核心忙碌的理想工作分配是僵硬地提前決定的。在統(tǒng)一著色器核心中,GPU可以決定如何平衡此負(fù)載。
描述整個(gè)著色器編程模型大大超出了本書(shū)的范圍,并且有許多文檔,書(shū),和網(wǎng)站已經(jīng)這么做了。著色器使用類似C(C-like)的著色語(yǔ)言進(jìn)行編程,比如DirectX的高級(jí)著色語(yǔ)言(HLSL)和OpenGL的著色語(yǔ)言(GLSL)。DirectX的HLSL可以被編譯成虛擬機(jī)字節(jié)碼(virtual machine bytecode),也叫做中間語(yǔ)言(IL或DXIL),以提供硬件獨(dú)立性。中間表示也可以允許著色器程序離線編譯和存儲(chǔ)。這種中間語(yǔ)言由驅(qū)動(dòng)程序轉(zhuǎn)換為特定GPU的ISA。控制臺(tái)編程通常避免中間語(yǔ)言步驟,因?yàn)槟菚r(shí)系統(tǒng)中只有一個(gè)ISA。
基本數(shù)據(jù)類型是32位單精度浮點(diǎn)數(shù)的標(biāo)量和向量,盡管向量只是著色器代碼的一部分,在硬件中并不支持就像
上面概述的。現(xiàn)代GPU同樣原生地支持32位整數(shù)和64位浮點(diǎn)數(shù)。浮點(diǎn)數(shù)向量通常包含數(shù)據(jù)如位置(xyzw),法線,矩陣行,顏色(rgba),或者紋理坐標(biāo)(uvwq)。整數(shù)通常用于表示計(jì)數(shù),索引,或者位掩碼。聚合數(shù)據(jù)類型例如結(jié)構(gòu),數(shù)組,和矩陣同樣也支持。
繪圖調(diào)用會(huì)調(diào)用圖形API來(lái)繪制一組圖元,因此導(dǎo)致圖形管道來(lái)執(zhí)行和運(yùn)行其著色器。每個(gè)可編程著色器階段有兩種類型的輸入:統(tǒng)一輸入(uniform inputs),這些值在整個(gè)繪圖調(diào)用中保持不變(但兩次繪圖調(diào)用間可以改變),和可變輸入(varying inputs),從三角形頂點(diǎn)或光柵化過(guò)來(lái)的數(shù)據(jù)。例如,像素著色器或許提供一個(gè)光源的顏色作為統(tǒng)一(uniform)值,三角形表面的位置每像素改變因此是可變值(varying)。一個(gè)紋理是一種特殊類型的統(tǒng)一(uniform)輸入,曾經(jīng)總是應(yīng)用于表面的一個(gè)顏色圖像,但是現(xiàn)在在這里可以被認(rèn)為是任意大數(shù)據(jù)的數(shù)組。
底層的虛擬機(jī)為不同類型的輸入和輸出提供特殊的寄存器。統(tǒng)一值(uniforms)可用的常量寄存器的數(shù)量遠(yuǎn)大于可變(varying)值或輸出可用的寄存器數(shù)量。這是因?yàn)榭勺?varying)輸入和輸出需要為每個(gè)頂點(diǎn)和像素單獨(dú)存放,因此對(duì)于需要多少個(gè)有一個(gè)自然的限制。統(tǒng)一(uniform)輸入只被存儲(chǔ)一次并且被繪圖調(diào)用內(nèi)的所有頂點(diǎn)和像素重用。虛擬機(jī)還具有通用臨時(shí)寄存器,用于暫存空間。可以使用臨時(shí)寄存器中的整數(shù)值對(duì)所有類型的寄存器進(jìn)行數(shù)組索引。著色器虛擬機(jī)的輸入和輸出如圖3.3所示。
圖形計(jì)算中常見(jiàn)的運(yùn)算可在現(xiàn)代GPU中高效的執(zhí)行。著色語(yǔ)言提供(expose)了這些操作中最常見(jiàn)的(例如加和乘)通過(guò)運(yùn)算符例如*和+。其余的通過(guò)固有的函數(shù)提供,即,atan(), sqrt(), log(), 和一些其它的,為GPU優(yōu)化的。還存在一些更復(fù)雜運(yùn)算的函數(shù),比如向量法線和反射,叉乘,和矩陣轉(zhuǎn)置和行列式的計(jì)算。
術(shù)語(yǔ)流程控制指的是使用分支指令改變代碼執(zhí)行流程。流程控制相關(guān)的指令用來(lái)實(shí)現(xiàn)高級(jí)語(yǔ)言構(gòu)造例如“if”和“case”語(yǔ)句,還有不同類型的循環(huán)語(yǔ)句。著色器支持兩種類型的流程控制。靜態(tài)流程控制分支基于統(tǒng)一(uniform)輸入的值。這意味著代碼流在整個(gè)繪制調(diào)用中是不變的。靜態(tài)流程控制的主要好處是允許可以在各種不同情況下使用的同一個(gè)著色器(即,不同數(shù)量的光源)。這里沒(méi)有線程分歧(thread divergence),因?yàn)樗械恼{(diào)用都采用相同的代碼路徑。動(dòng)態(tài)流程控制基于可變(varying)輸入的值,意味著每個(gè)片段可以不同地執(zhí)行代碼。這比靜態(tài)流程控制強(qiáng)大的多但會(huì)降低性能,尤其是如果代碼流在著色器調(diào)用間不規(guī)則地改變。
總結(jié)
以上是生活随笔為你收集整理的预处理器命令必须作为第一个非空白空间启动_第三章 图形处理器(上)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
 
                            
                        - 上一篇: 大数据是什么工作 一般有这些职位
- 下一篇: 京东白条可以提现吗
