天天看點

GPU程式設計之進擊的優化系列一裝置微架

第一章裝置微架構

1.0CUDA裝置

1.0.0 核心微架構

1.0.1 指令編碼格式

1.0.2 寄存器檔案結構

1.0.3 指令流水線

1.0.4 Pascal架構(待續)

1.1 GCN裝置

1.1.0 核心微架構

1.1.1 指令編碼格式

1.1.2 寄存器檔案結構

1.1.3 指令流水線

1.2 GPU裝置上的條件分支

第一章     裝置微架構

前言

              第一章我們介紹CUDA裝置和GCN裝置的微架構做。對裝置微架構的了解可以在深度優化時提供理論依據和方向指導,對微架構方面細節的掌握有時甚至是幫助某些應用達到最優性能必須要的。當然,對底層架構細節的了解并不是必須的,若讀者對這些内容沒有興趣,可以跳過本章。

1.0  CUDA裝置微架構

kepler架構包含了數組SMX,每個SMX有以下功能單元構成:

1 指令預取單元和微指令緩沖區

2 4個warp排程器,每個warp排程器對應兩個指令分派單元

3 192個CUDA Core和64或8個雙精度計算單元

4 32個超越函數計算單元(SFU)

5 分支邏輯控制單元

6 32個LD/ST存儲器通路單元

7 片上緩存結構,包括共享記憶體,L1緩存,紋理緩存,常量記憶體以及隻讀緩存,不同的裝置大小可能不同

                                        kepler裝置SMX微架構圖

GPU程式設計之進擊的優化系列一裝置微架

23.jpg (601.25 KB, 下載下傳次數: 0)

2016-6-24 10:49 上傳

    各種指令在不同的功能單元上執行,大緻可分為四類:簡單計算指令,複雜計算指令,分支指令和訪存指令,下面是各個單元所支援的操作(僅以kepler和maxwell裝置為例)

CUDA Core :32位單精度浮點加法,乘法,積和融加運算;32位整數加法;32位資料的比較操作,最小和最大操作;32位資料的位邏輯操作(and,or, xor);8位,16位資料和32位資料之間的轉換操作。

雙精度計算單元:雙精度浮點加法,乘法,積和融加運算。

SFU:整數乘法,除法;單精度浮點數除法以及基本的數學函數如正弦,餘弦和指數等操作;popc,clz,brev,bfe和bfi操作。

分支邏輯控制單元:分支,跳轉等邏輯操作。

LD/ST單元:全局記憶體,共享記憶體,局部記憶體,常量記憶體,紋理加載等存儲器通路操作(寄存器資料的通路無需通過LD/ST單元)。

warp vote和warpshuffle操作時在是專門的組合邏輯單元上完成的。

       maxwell裝置和kepler類似,但是每個SMM裡包含了四組獨立的warp計算單元,每個warp單元包含了一個微指令緩沖區,兩個指令分派單元,1個warp排程器,32個CUDACore,1個雙精度計算單元,8個單精度SFU,4個紋理單元,64k個32位寄存器組成的寄存器檔案以及24k紋理緩存。所有四個warp單元共享一個指令緩存以及64k~96k的共享/L1緩存。雖然每個SMM中的CUDACore數量少于SMX,但是每個計算單元具有更高的性能功耗比。相對來說,maxwell具有更優秀的單精度計算效能,但為了平衡性能功耗比,所有計算能力的maxwell裝置對雙精度計算的支援都十分有限,而kepler更适合那些需要雙精度計算的專業領域。每三個SMX或每四個SMM組成一個GPC,所有GPC共享512k~2M的二級緩存。

緩存小結:

對于計算能力2.x的裝置,紋理管線完全獨立于L1/L2緩存結構。

對于計算能力3.x的裝置,紋理和L1緩存是各自獨立的,但都通過L2緩存加載全局記憶體資料。

對于計算能力5.x的裝置,紋理緩存和L1緩存是統一的,所有對全局資料的通路都通過L2緩存路徑,除非采用直寫政策。

                                        maxwell裝置微架構圖

GPU程式設計之進擊的優化系列一裝置微架

2.png (223.64 KB, 下載下傳次數: 0)

2016-6-24 10:50 上傳

kepler或maxwell裝置的指令編碼長度在32位系統和64位系統下均為64位無符号整型資料。指令的編碼中包含了操作碼,寄存器索引以及浮點計算的舍入模式等位域資訊。而溢出标志位,進位标志位,零标志等則用一個特殊的條件寄存器存放。很可惜,NVIDIA并未公開指令集編碼格式的詳細資訊。但這并不妨礙我們做出合理的猜測并通過大量實驗對猜測進行驗證(順便吐槽下,AMD在這方面做的厚道多了)。

正因為寄存器索引包含在指令編碼中的,且距離計算單元最近,是以其延遲比其它任何類型的記憶體都要低,帶寬也要遠高于其它類型的記憶體,這也正是為什麼充分使用寄存器是某些應用達到峰值性能所必須且唯一的原因。在計算能力3.5+的kepler裝置和maxwell裝置上的每個SM中的64k個32-bit寄存器檔案被劃分為4個bank,每個bank中的16k個32-bit寄存器共享32個通道(lane),每個通道32位寬。由于每個線程最多可使用255個寄存器,那麼如果使用單個bank的話,每個源或目标寄存器在指令編碼中需要占用8位;如果使用4個bank,那麼每個目标寄存器或是源寄存器隻需占用6位,剩下的位數可以留作它用。比如對與四操作數指令(比如FMA),如果采用單個bank結構的寄存器檔案,那麼寄存器索引在指令編碼中需要占用32位;而采用4-bank結構的寄存器檔案的話,寄存器索引在指令編碼中隻需要占用24位,節省了8位。不同的架構寄存器bank的數和編碼規則可能不同,如在keplerGPU上,寄存器bank編碼為:

              bank0 :            R0,      R2,      R8,     R10,    …

              bank1 :            R1,      R3,      R9 ,     R11,   …

              bank2:            R4,      R6,      R12,     R14,    …

              bank3 :            R5,      R7,      R13,     R15,   …

在maxwell GPU上,寄存器bank編碼為:

                        bank0 :            R0,      R4,     R8,      R12,   …

                        bank1:             R1,     R5,      R9,     R13,    …

                        bank2:             R2,      R6,      R10,     R14,    …

                        bank3:             R3,      R7,      R11,     R15,    …

    了解裝置的寄存器檔案結構對于性能分析以及深度優化具有至關重要的作用,因為不合理的寄存器配置設定會造成bank沖突(bank-conflict),類似于共享記憶體的bank沖突;隻有在沒有bank沖突的情況下,諸如FMUL,FMA,FADD等操作才能全速執行,否則會花費更多的指令周期讀取寄存器資料,增加的時鐘延遲大小和寄存器的bank沖突數成正比。下面以maxwell-gpu為例:

              FMA   R0,    R7,    R3,    R1

      FMA    R14,    R9,     R1,     R10

分别具有1-way bank和2-way沖突。如果調整寄存的配置設定如下

              FMA   R0,    R6,    R3,    R1

              FMA    R14,    R9,      R3,    R12

則可以消除bank沖突。

使用矢量加載可以一定程度的控制寄存器按照某種對齊方式進行配置設定,但也通常會妨礙編譯器進行寄存器bank優化,因為矢量加載指令要求第一個寄存器必須是加載寬度(按32-bit元素的個數算)的模0,如:

LD.E.128       R0,     [R13]

LDS.U.128      R8,     [R17]

{R0, R8}, {R1, R9},{R2, R10}, {R3, R11} 分别對應于bank0, bank1, bank2, bank3;          寄存器檔案配置設定需要注意的另一個問題是bank對齊,簡單來說就是在為指令配置設定寄存器時,相鄰的指令中位于同一個bank中的寄存器最好是具有相同的位置,例如

                        FMA  R3,  R0, R1,  R2

                        FMA  R7, R4,  R5,  R6

這樣可以将寄存器對齊到操作數端口上。編譯器也往往在在指令間插入MOV操作來達到操作數端口對齊或是消除寄存器bank沖突的目的

                        FADD  R2,      R0,      R1

                        FADD  R6,      R1,     R5

                                    =>

          MOV    R4,      R1

                        FADD  R2,      R4,     R5

   L1緩存,常量記憶體,共享記憶體以及紋理緩存的資訊在很多資料中都有詳細的叙述,這裡不再多說,有興趣的讀者可查閱本章後面的參考資料。

首先指令預取單元從指令緩存(片上的緩存,實體存儲媒體和L1緩存相同)中取出多條指令進行譯碼,然後将譯碼後的指令存儲在隸屬warp排程器的微指令緩沖區中。指令發射單元從微指令緩沖區中取出兩條指令分派給warp排程器標明的warp指令管線中,兩條同類指令是順序進入管線的,指令管線通過将一條指令的發射,取操作數時鐘和另一條指令的取操作數和在功能單元上的執行時鐘重疊來流水線化指令的操作,這樣可以有效降低整個指令流水期間所有指令的總時鐘數。流水線化在多個層次都同時存在,如不同功能單元之間的流水化,warp單元和warp單元之間的流水化以及指令和指令之間的流水化。不同功能單元之間的流水化,比如将計算單元上的操作和LD/ST單元上的操作重疊進行(不同功能單元在沒有資料依賴的情況下可以真正同時執行)來隐藏資料通路的延遲。通過多個warp之間的切換讓處于停頓延遲中的warp和正在執行的warp的時鐘重疊隐藏warp中的計算或是訪存延遲,亦即将warp流水化。單條指令在管線中的過程分為發射,執行和寫回。發射階段從寄存器檔案中取操作數;在執行階段根據指令中的操作碼在相應的單元上執行特定的操作;寫回階段則将計算結果寫入目标寄存器。是以,指令之間的流水化正是通過将前一條指令的執行和寫回與下一條指令的發射和執行的時鐘重疊來減少整個管線中所有指令的操作延遲。在指令譯碼階段完成資料的存取後指令的計算結果并非立即可用,而是需要數個時鐘的延遲。一般來說指令管線的深度至少不應小于指令的計算延遲,才足以保證指令流過管線後已經完成計算。理想情況下,每當一條指令從指令管線末端流出(完成計算)時,管線的前端就會流入一條新的指令,這樣指令管線中的多條指令通過時鐘重疊的方式有效的隐藏計算延遲(流水線中的N條指令的總延遲為N*發射延遲+1*計算延遲)。不同的指令可能具有不同的指令發射周期,亦即當發射一條指令後,需要幾個時鐘的停頓後才會發射下一條同類指令。我們假設一個指令管線的深度和指令的執行延遲均為4個時鐘周期,每周期均可發射1條指令,那麼指令流水線情況如下

     1 clock I0

     2 clockI1  I0

     3 clockI2  I1 I0

     4 clockI3  I2 I1  I0

     5clock     I3  I2 I1  I0

6 clock        I3  I2  I1  I0

     7 clock                      I3 I2  I1  I0

     8clock                I3  I2 I1  I0

               流水線化,紅色部分是從管線末端流出的以完成操作

如果沒有采用流水線化,那麼

      1~ 5 clock I0

      6~10clock               I1

     11~15clock                      I2

     16~20clock                                I3

可以看出,采用流水線化的操作完成四條指令的譯碼和執行隻需要8個時鐘周期,而不采用流水線化的操作則需要20(4*(1*issue-clock+4*execute-clock))個時鐘周期。

1.1       GCN裝置微架構

    GCN裝置上的每個Compute Unit具有256k個32bit矢量寄存器檔案用于向量單元上的浮點計算,同時每個計算單元各有512個32bit标量寄存器用于邏輯分支,位址計算等标量操作。向量寄存器檔案被劃分成4個bank,各自分屬ComputeUnit内的4組向量計算單元使用。不同于kepler和maxwell架構,GCN架構上4個寄存器bank各自分屬不同的16-wide向量計算單元,是以不存在寄存器bank沖突的問題。每個bank有16(不同的裝置可能不同)個通道,通道寬度是4位元組。當使用8位元組或16位元組的資料時,使用連續的2個或4個寄存器,是以寄存器的配置設定滿足對其要求。對于8位元組資料,寄存器編号需要對齊到2的倍數;對于16位元組資料,寄存器編号需要對齊到4的倍數;是以當使用向量類型時有時會增加寄存器的使用數量,這一點類似于記憶體的對齊配置設定會比普通的配置設定略大。

    這裡并不假定任何型号的裝置,是以這一節所講的同時适用于CUDA裝置和GCN裝置。每個warp或wavefront擁有一組條件掩碼寄存器堆棧(堆棧中包含了多個掩碼寄存器,每個掩碼寄存器32位(CUDA)或64位(GCN),每位對應一個線程的掩碼),GCN裝置上的掩碼寄存器堆棧深度為6。假設我們有一個深度為為3的嵌套分支,那麼warp或wavefront中線程的執行過程為

      1 首先将計算得到的最外層的條件掩碼寫入掩碼寄存器堆棧頂部的第1個寄存器(掩碼堆棧指針目前指向的位置)。

      2 掩碼寄存器堆棧指針減1,接着将計算得到的第2層分支的條件掩碼寫入掩碼寄存器堆棧的下一個寄存器(掩碼堆棧指針目前指向的)。

      3 掩碼寄存器堆棧指針減1,接着将計算得到的第3層分支的條件掩碼寫入掩碼寄存器堆棧的下一個寄存器。

      4 從掩碼寄存器堆棧指針指向的位置取出掩碼,如果warp或wavefront中的線程号對應的位值為1,則執行第3層分支内的計算,否則忽略。

      5 掩碼寄存器堆棧指針加1,取出掩碼寄存器中的掩碼,如果warp或wavefront中的線程号對應的位值為1,則執行第2層分支内的計算,否則忽略。

6 掩碼寄存器堆棧指針加1,取出掩碼寄存器中的掩碼,如果warp或wavefront中的線程号對應的位值為1,則執行第1層分支内的計算,否則忽略。

小結

    本章我們分别介紹了CUDA裝置和GCN裝置的微架構,重點分析了寄存器檔案結構和指令管線以及GPU裝置上實作條件分支的大概原理。深入了解這些内容不僅可以讓你知道指令在硬體中具體的行為以及為什麼會這樣,同時還會在優化過程中的某個角落為你說明性能無法進一步提升的障礙所在。

    通常了解和使用所有這些内容需要一個漫長的過程,建議經常到裝置商家官方網站上下載下傳最新的技術資料,同時一些編譯原理和計算機結構方面的書籍也會幫到你,因為很多技術原理無論在CPU還是GPU上都是通用的。

參考資料

      1《CUDA Programming Guide》

2《CUDA BinaryUtilities》

3《NVIDIA KeplerGK110 Whitepaper》

4《NVIDIA GeforceGTX980 Whitepaper》

5《AMDGCN Architecture Whitepaper》

6《AMD Southern Island Series Instruction SetArchitecture》

7《Graphics Core Next Architecture,Generation 3》

8《Compute Systems A programmer’s Perspective》,Randal E.Bryant, DavidR.O’Hallaron

9Performance Upper Bound Analysis and Optimization of SGEMM on Fermi and KeplerGPUs,Junjie Lai, Andr_e Seznec

原文釋出時間為: 2016-6-24 10:55:51

原文由:nvadmin 釋出,版權歸屬于原作者 

本文來自雲栖社群合作夥伴NVIDIA,了解相關資訊可以關注NVIDIA官方網站

繼續閱讀