GPU核心技術開發


GPU核心技術開發

由於上一節主要闡述GPU內部的工作流程和機制,為了簡潔性,省略了很多知識點和過程,本節將對它們做進一步補充說明。

1.  CUDA技術

1)NVIDIA CUDA 是什么?

NVIDIA CUDA 是 NVIDIA 並行計算架構在 GPU 中的名稱。NVIDIA 提供了 NVIDIA CUDA 架構編程的全套工具包,其中包括編譯器、調試器、分析器、庫以及開發者交付運用 CUDA 架構的生產質量產品所需的其它信息。NVIDIA CUDA 架構也支持 C 和 Fortran 等標准語言,以及 OpenCL 和 DirectCompute 等用於 GPU 計算的 API。

使用 NVIDIA CUDA 的性能提升如何?

這要取決於具體問題在該架構上的映射情況。對於數據並行應用,提速 10 倍到 200 倍不等。

2NVIDIA CUDA 支持哪些操作系統?

NVIDIA CUDA 支持 Windows XP、Windows Vista、Windows 7、Linux 和 OS X。這些操作系統的 32 位和 64 位版本都支持。

哪些應用支持NVIDIA CUDA ?

一些消費類應用示例

·      Badaboom – 視頻編碼

·      MotionDSP – vReveal (視頻增強 – 降噪、提高分辨率、穩定圖像)

·      ArcSoft – SimHD (提升到高清)

·      CyberLink– PowerDirector 7 (編碼、視頻過濾)

·      Pegasys – TMPGEnc 4.0 Xpress (視頻過濾,包括降噪、銳化)

·      SETI@home (分析搜尋外星人的無線電望遠鏡信號)

對於游戲玩家而言,「鏡之邊緣」、「聖域2」以及「雪域危機」等游戲可以用 NVIDIA CUDA 來進行 PhysX 游戲加速

3)CUDA 是一種編程語言嗎?

CUDA 是我們用於 GPU 計算的架構,能在 GPU 上運行標准 C 語言。為實現這一點,NVIDIA 定義了一套通用計算指令集 (PTX) 和一小部分C語言擴展集,從而讓開發者充分利用我們 GPU 中強大的並行計算能力。Portland Group 為 NVIDIA CUDA 架構上的 Fortran 提供支持,而其它一些公司則為 Java、Python、.NET 等其它語言提供支持。

我們用術語 “CUDA C” 來描述開發者指定 GPU 上要執行的功能、GPU 內存如何使用、應用程序如何使用 GPU 的並行處理功能所使用的語言和一小部分擴展集。

NVIDIA 的 C 語言編譯器是使用 Edison Design Group C 語言分析器及 Open64 編譯器構建的,並且它進行了擴展以支持 CUDA C 擴展。很多CPU公司在他們的編譯器中都廣泛使用 EDG 分析器和 Open64 編譯器。

4)GPU 編程難嗎?

這實際上就是重要代碼的並行處理難不難的問題。無論是對於 CPU 還是對於 GPU,並行處理的行業難題都在於確定哪些算法占用了大量的計算時間 (關鍵路徑),並且只移植這些將擴展的算法。

NVIDIA CUDA 架構大大減輕了手動管理並行機制的負擔。為 NVIDIA CUDA 架構編寫的算法實際上就是可以在多個不同處理器上同步運行的串行算法,常常稱之為“內核”。GPU 通過在 GPU 中的多個處理器上啟動成千上萬個實例,來提取該內核並對它執行並行處理。由於大多數算法是作為串行算法啟動的,因此移植程序到 NVIDIA CUDA 架構不費吹灰之力,就和使用 CUDA C 將一個循環轉換為一個 CUDA 內核一樣簡單。不必像對待現代多核 CPU 一樣,將整個程序完全重構為多線程程序。

OpenCL 和 DirectCompute 在 NVIDIA CUDA 架構所使用的編程模式方面極其相似,很多開發者認為這種編程模式比並行處理的其它方法更容易獲得出色、可擴展的性能。

5)現在有學生可以學的 GPU 計算課程嗎?

在伊利諾伊大學上了堂計算課程的 12 個月內,就有300多所大學、學院和學校開設使用 CUDA SDK 和工具包的並行編程課程。另外,還有 1000 多所大學開設了一般性的並行編程課程,為學習如何對其專攻領域的算法應用並行處理的學生傳授基礎知識。這是幾十年來計算機科學教學領域發生的更大轉變之一。

6)NVIDIA 支持 OpenCL 或 DirectCompute 嗎?

支持所有標准 API。在 GDC 09 上,NVIDIA 展示了用我們的 C 語言編程環境以及 API、OpenCL、DirectCompute 編寫的程序示例。OpenCL 是在 NVIDIA GPU 上開發的,而 NVIDIA 在十二月份舉行的亞洲 SIGGRAPH 大會上率先展示了在 GPU 上運行的 OpenCL 應用程序。2009 年 8 月,NVIDIA 針對戰略開發商發布了 OpenCL 驅動程序。同時 NVIDIA 也是在其 GPU 中率先提供 DirectCompute 支持的公司。

7)NVIDIA CUDA 與 OpenCL 有何關聯?

OpenCL 及其它編程界面都是受 CUDA C 編程模式的啟發。OpenCL、DirectCompute、CUDA C 和 Portland Group 的 CUDA Fortran 擴展都是使用相似的理念來將並行應用程序移植到 GPU 上。

8)NVIDIA 推薦開發者使用的語言有哪些? 是 OpenCL、CUDA C,還是 CUDA FORTRAN 或 DirectCompute? 為什么現在有了 OpenCL 有些開發者還是堅持使用 CUDA C? 它對開發者有什么好處?

這要歸結於個人喜好。開發者喜歡使用他們更感得心應手的編程界面,也就是能支持他們慣用的開發環境、庫以及操作系統的編程界面。

由於 NVIDIA 和 CUDA 架構支持以上所有語言,因此選擇哪種語言完全在於開發者本人。開發者對編程環境的選擇基於這幾個典型的問題:要開始編碼的時間、現在所使用的編程語言、需要支持的操作系統、需要實施的其它代碼或庫、供應商技術支持、開發者使用的傳統代碼等等。

隨着其它語言和 API 的支持逐漸成熟,開發者可以根據需要來移植代碼,因為很多編程理念都是相似的。現今 CPU 的開發就是這樣,沒人強迫開發者使用 C、C++、C#、Java – 而是由開發者自行選擇,而更終開發者都會受益,因為他們的選擇空間非常大。

2.   SIMDSIMT

SIMDSingle Instruction Multiple Data)是單指令多數據,在GPU的ALU單元內,一條指令可以處理多維向量(一般是4D)的數據。比如,有以下shader指令:

float4 c = a + b; // a, b都是float4類型

對於沒有SIMD的處理單元,需要4條指令將4個float數值相加,匯編偽代碼如下:

ADD c.x, a.x, b.x

ADD c.y, a.y, b.y

ADD c.z, a.z, b.z

ADD c.w, a.w, b.w

但有了SIMD技術,只需一條指令即可處理完:

SIMD_ADD c, a, b

  SIMTSingle Instruction Multiple Threads,單指令多線程)是SIMD的升級版,可對GPU中單個SM中的多個Core同時處理同一指令,並且每個Core存取的數據可以是不同的。

SIMT_ADD c, a, b

上述指令會被同時送入在單個SM中被編組的所有Core中,同時執行運算,但a、b 、c的值可以不一樣:

 3.  co-issue

co-issue是為了解決SIMD運算單元無法充分利用的問題。例如下圖,由於float數量的不同,ALU利用率從100%依次下降為75%、50%、25%。

 為了解決着色器在低維向量的利用率低的問題,可以通過合並1D與3D或2D與2D的指令。例如下圖,DP3指令用了3D數據,ADD指令只有1D數據,co-issue會自動將它們合並,在同一個ALU只需一個指令周期即可執行完。

 但是,對於向量運算單元(Vector ALU),如果其中一個變量既是操作數又是存儲數的情況,無法啟用co-issue技術:

 於是標量指令着色器Scalar Instruction Shader)應運而生,它可以有效地組合任何向量,開啟co-issue技術,充分發揮SIMD的優勢。

4.  if - else語句

 如上圖,SM中有8個ALU(Core),由於SIMD的特性,每個ALU的數據不一樣,導致if-else語句在某些ALU中執行的是true分支(黃色),有些ALU執行的是false分支(灰藍色),這樣導致很多ALU的執行周期被浪費掉了(即masked out),拉長了整個執行周期。最壞的情況,同一個SM中只有1/8(8是同一個SM的線程數,不同架構的GPU有所不同)的利用率。

同樣,for循環也會導致類似的情形,例如以下shader代碼:

void func(int count, int breakNum)

{

        for(int i=0; i<count; ++i)

        {

                 if (i == breakNum)

                         break;

                 else

                         // do something

        }

}

由於每個ALU的count不一樣,加上有break分支,導致最快執行完shader的ALU可能是最慢的N分之一的時間,但由於SIMD的特性,最快的那個ALU依然要等待最慢的ALU執行完畢,才能接下一組指令的活!也就白白浪費了很多時間周期。

5.  Early-Z

早期GPU的渲染管線的深度測試是在像素着色器之后才執行(下圖),這樣會造成很多本不可見的像素執行了耗性能的像素着色器計算。

 后來,為了減少像素着色器的額外消耗,將深度測試提至像素着色器之前(下圖),這就是Early-Z技術的由來。

 Early-Z技術可以將很多無效的像素提前剔除,避免它們進入耗時嚴重的像素着色器。Early-Z剔除的最小單位不是1像素,而是像素塊pixel quad,2x2個像素,詳見[4.3.6 ](#4.3.6 像素塊(pixel quad)))。

但是,以下情況會導致Early-Z失效:

·       開啟Alpha Test:由於Alpha Test需要在像素着色器后面的Alpha Test階段比較,所以無法在像素着色器之前就決定該像素是否被剔除。

·       開啟Alpha Blend:啟用了Alpha混合的像素很多需要與frame buffer做混合,無法執行深度測試,也就無法利用Early-Z技術。

·       開啟Tex Kill:即在shader代碼中有像素摒棄指令(DX的discard,OpenGL的clip)。

·       關閉深度測試Early-Z是建立在深度測試看開啟的條件下,如果關閉了深度測試,也就無法啟用Early-Z技術。

·       開啟Multi-Sampling:多采樣會影響周邊像素,而Early-Z階段無法得知周邊像素是否被裁剪,故無法提前剔除。

·       以及其它任何導致需要混合后面顏色的操作。

此外,Early-Z技術會導致一個問題:深度數據沖突depth data hazard)。

例子要結合上圖,假設數值深度值5已經經過Early-Z即將寫入Frame Buffer,而深度值10剛好處於Early-Z階段,讀取並對比當前緩存的深度值15,結果就是10通過了Early-Z測試,會覆蓋掉比自己小的深度值5,最終frame buffer的深度值是錯誤的結果。

避免深度數據沖突的方法之一是在寫入深度值之前,再次與frame buffer的值進行對比:

 6.  統一着色器架構(Unified shader Architecture)

在早期的GPU,頂點着色器和像素着色器的硬件結構是獨立的,它們各有各的寄存器、運算單元等部件。這樣很多時候,會造成頂點着色器與像素着色器之間任務的不平衡。對於頂點數量多的任務,像素着色器空閑狀態多;對於像素多的任務,頂點着色器的空閑狀態多(下圖)。

 於是,為了解決VS和PS之間的不平衡,引入了統一着色器架構(Unified shader Architecture)。用了此架構的GPU,VS和PS用的都是相同的Core。也就是,同一個Core既可以是VS又可以是PS。

 這樣就解決了不同類型着色器之間的不平衡問題,還可以減少GPU的硬件單元,壓縮物理尺寸和耗電量。此外,VS、PS可還可以和其它着色器(幾何、曲面、計算)統一為一體。

 7.  像素塊(Pixel Quad)

32個像素線程將被分成一組,或者說8個2x2的像素塊,這是在像素着色器上面的最小工作單元,在這個像素線程內,如果沒有被三角形覆蓋就會被遮掩,SM中的warp調度器會管理像素着色器的任務。

也就是說,在像素着色器中,會將相鄰的四個像素作為不可分隔的一組,送入同一個SM內4個不同的Core。

為什么像素着色器處理的最小單元是2x2的像素塊?

筆者推測有以下原因:

1)簡化和加速像素分派的工作。

2)精簡SM的架構,減少硬件單元數量和尺寸。

3)降低功耗,提高效能比。

4)無效像素雖然不會被存儲結果,但可輔助有效像素求導函數。這種設計雖然有其優勢,但會激化過繪制(Over Draw)的情況,損耗額外的性能。比如下圖中,白色的三角形只占用了3個像素(綠色),按普通的思維,只需要3個Core繪制3次就可以了。

 但是,由於上面的3個像素分別占據了不同的像素塊(橙色分隔),實際上需要占用12個Core繪制12次(下圖)。

  這就會額外消耗300%的硬件性能,導致了更加嚴重的過繪制情況。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM