揭秘英偉達 GPU 架構演進近十年,從費米到安培

作者:tomoyazhang,騰訊 PCG 後臺開發工程師

,騰訊 PCG 後臺開發工程師

隨著軟體從 1。0 進化到 2。0,也即從圖靈機演進到類深度學習演算法。計算用的硬體也在加速從 CPU 到 GPU 等遷移。本文試圖整理從英偉達 2010 年開始,到 2020 年這十年間的架構演進歷史。

CPU and GPU

我們先對 GPU 有一個直觀的認識,如下圖:

揭秘英偉達 GPU 架構演進近十年,從費米到安培

眾所周知,由於儲存器的發展慢於處理器,在 CPU 上發展出了多級快取記憶體的結構,如上面左圖所示。而在 GPU 中,也存在類似的多級快取記憶體結構。只是相比 CPU,GPU 將更多的電晶體用於數值計算,而不是快取和流控(Flow Control)。這源於兩者不同的設計目標,CPU 的設計目標是並行執行幾十個執行緒,而 GPU 的目標是要並行執行幾千個執行緒。

可以在上面右圖看到,GPU 的 Core 數量要遠遠多餘 CPU,但是有得必有失,可以看到 GPU 的 Cache 和 Control 要遠遠少於 CPU,這使得 GPU 的單 Core 的自由度要遠遠低於 CPU,會受到諸多限制,而這個限制最終會由程式設計師承擔。這些限制也使得 GPU 程式設計與 CPU 多執行緒程式設計有著根本區別。

這其中最根本的一個區別可以在上右圖中看出,每一行有多個 Core,卻只有一個 Control,這代表著多個 Core 同一時刻只能執行同樣的指令,這種模式也稱為 SIMT (Single Instruction Multiple Threads)。 這與現代 CPU 的 SIMD 倒是有些相似,但卻有根本差別,本文在後面會繼續深入細究。

從 GPU 的架構出發,我們會發現,因為 Cache 和 Control 的缺失,只有

計算密集

資料並行

的程式適合使用 GPU。

計算密集:

數值計算

的比例要遠大於

記憶體操作

,因此記憶體訪問的延時可以被計算掩蓋,從而對 Cache 的需求相對 CPU 沒那麼大。

資料並行: 大任務可以拆解為執行相同指令的小任務,因此對複雜流程控制的需求較低。

而深度學習恰好滿足以上兩點,本人認為,即使存在比深度學習計算量更低且表達能力更強的模型,但如果不滿足以上兩點,都勢必打不過 GPU 加持下的深度學習。

Fermi

Fermi 是 Nvidia 在 2010 年釋出的架構,引入了很多今天也仍然不過時的概念,而比 Fermi 更早之前的架構,也已經找不到太多資料了,所以本文從 Fermi 開始,先來一張總覽。

揭秘英偉達 GPU 架構演進近十年,從費米到安培

GPU 透過 Host Interface 讀取 CPU 指令,GigaThread Engine 將特定的資料從 Host Memory 中複製到內部的 Framebuffer 中。隨後 GigaThread Engine 建立並分發多個 Thread Blocks 到多個 SM 上。多個 SM 彼此獨立,並獨立排程各自的多個 Thread Wraps 到 SM 內的 CUDA Cores 和其他執行單元上執行。

上面這句話有幾個概念解釋一下:

SM: 對應於上圖中的 SM 硬體實體,內部有很多的 CUDA Cores;

Thread Block: 一個 Thread Block 包含多個執行緒(比如幾百個),多個 Blocks 之間的執行完全獨立,硬體可以任意排程多個 Block 間的執行順序,而 Block 內部的多個執行緒執行規則由程式設計師決定,程同時程式設計師可以決定一共有多少個 Blocks;

Thread Warp: 32 個執行緒為一個 Thread Warp,Warp 的排程有特殊規則,本文後面會繼續深入。

由於本文不是講怎麼寫 CUDA,所以如果對 SM/Block 的解釋仍然不明白,可以參考這一小節:

https://docs.nvidia.com/cuda/cuda-c-programming-guide/index.html#scalable-programming-model

上圖存在 16 個 SMs,每個 SM 帶 32 個 Cuda Cores,一共 512 個 Cuda Cores。 這些數量不是固定的,和具體的架構和型號相關。

接下來我們深入看 SM,來一張 SM 總覽:

揭秘英偉達 GPU 架構演進近十年,從費米到安培

從上圖可知,SM 內有 32 個 CUDA Cores,每個 CUDA Core 含有一個 Integer arithmetic logic unit (ALU)和一個 Floating point unit(FPU)。 並且提供了對於單精度和雙精度浮點數的 FMA 指令。

SM 內還有 16 個 LD/ST 單元,也就是 Load/Store 單元,支援 16 個執行緒一起從 Cache/DRAM 存取資料。

4 個 SFU,是指 Special Function Unit,用於計算 sin/cos 這類特殊指令。每個 SFU 每個時鐘週期只能一個執行緒執行一條指令。而一個 Warp(32 執行緒)就需要執行 8 個時鐘週期。SFU 的流水線是從 Dispatch Unit 解耦的,所以當 SFU 被佔用時,Dispatch Unit 會去使用其他的執行單元。

之前一直提到 Warp,但之前只說明瞭是 32 個執行緒,我們在這裡終於開始詳細說明,首先來看 Dual Warp Scheduler 的概覽。

揭秘英偉達 GPU 架構演進近十年,從費米到安培

在之前的 SM 概覽圖以及上圖裡,可以注意到 SM 內有兩個 Warp Scheduler 和兩個 Dispatch Unit。 這意味著,同一時刻,會併發執行兩個 warp,每個 warp 會被分發到一個 Cuda Core Group(16 個 CUDA Core), 或者 16 個 load/store 單元,或者 4 個 SFU 上去真正執行,且每次分發只執行

一條

指令,而 Warp Scheduler 維護了多個(比如幾十個)的 Warp 狀態。

這裡引入了一個核心的約束,任意時刻,一個 Warp 裡的 Thread 都在執行同樣的指令,對於程式設計師來說,觀測不到一個 warp 裡不同 thread 的不同執行情況。

但是眾所周知,不同執行緒可能會進入不同的分支,這時如何執行一樣的指令?

揭秘英偉達 GPU 架構演進近十年,從費米到安培

可以看上圖,當發生分支時,只會執行進入該分支的執行緒,如果進入該分支的執行緒少,則會發生資源浪費。

在 SM 概覽圖裡,我們可以看到 SM 內 64KB 的 On-Chip Memory,其中 48KB 作為 shared memory, 16KB 作為 L1 Cache。 對於 L1 Cache 以及非 On-Chip 的 L2 Cache,其作用與 CPU 多級快取結構中的 L1/L2 Cache 非常接近,而 Shared Memory,則是相比 CPU 的一個大區別。無論是 CPU 還是 GPU 中的 L1/L2 Cache,一般意義上都是無法被程式設計師排程的,而 Shared Memory 設計出來就是讓渡給程式設計師進行排程的片上快取記憶體。

Kepler

2012 年 NVIDIA 釋出了 Kepler 架構,我們直接看使用 Kepler 架構的 GTX680 概覽圖:

揭秘英偉達 GPU 架構演進近十年,從費米到安培

可以看到,首先 SM 改名成了 SMX,但是所代表的概念沒有大變化,我們先看看 SMX 的內部:

揭秘英偉達 GPU 架構演進近十年,從費米到安培

還是 Fermi 中熟悉的名詞,就是數量變多了很多。

本人認為這個 Kepler 架構中最值得一提的是 GPUDirect 技術,可以繞過 CPU/System Memory,完成與本機其他 GPU 或者其他機器 GPU 的直接資料交換。畢竟在 2021 年的當今,Bypass CPU/OS 已經是最重要加速手段之一。

Maxwell

2014 年 NVIDIA 釋出了 Maxwell 架構,我們直接看架構圖:

揭秘英偉達 GPU 架構演進近十年,從費米到安培

可以看到,這次的 SM 改叫 SMM 了,Core 更多了,也更強大了,這裡就不過多介紹了。

Pascal

2016 年 NVIDIA 釋出了 Pascal 架構,這是第一個考慮 Deep Learning 的架構,也是一個值得大書筆墨的架構,首先看如下圖 P100。

揭秘英偉達 GPU 架構演進近十年,從費米到安培

可以看到,還是一如既往地增加了很多 Cores, 我們細看 SM 內部:

揭秘英偉達 GPU 架構演進近十年,從費米到安培

單個 SM 只有 64 個 FP32 Cuda Cores,相比 Maxwell 的 128 和 Kepler 的 192,這個數量要少很多,並且 64 個 Cuda Cores 分為了兩個區塊。需要注意的是,Register File 的大小並未減少,這意味著每個執行緒可以使用的暫存器更多了,而且單個 SM 也可以併發更多的 thread/warp/block。 由於 Shared Memory 並未減少,同樣意味著每個執行緒可以使用的 Shared Memory 及其頻寬都會變大。

增加了 32 個 FP64 Cuda Cores, 也就是上圖的 DP Unit。 此外 FP32 Cuda Core 同時具備處理 FP16 的能力,且吞吐率是 FP32 的兩倍,這卻是為了 Deep Learning 準備的了。

這個版本引入了一個很重要的東西:NVLink。

隨著單 GPU 的計算能力越來越難以應對深度學習對算力的需求,人們自然而然開始用多個 GPU 去解決問題。從單機多 GPU 到多機多 GPU,這當中對 GPU 互連的頻寬的需求也越來越多。多機之間,採用 InfiniBand 和 100Gb Ethernet 去通訊,在單機內,特別是從單機單 GPU 到達單機 8GPU 以後,PCIe 的頻寬往往就成為了瓶頸。為了解決這個問題,NVIDIA 提供了 NVLink 用以單機內多 GPU 內的點到點通訊,頻寬達到了 160GB/s, 大約 5 倍於 PCIe 3 x 16。 下圖是一個典型的單機 8 P100 拓撲。

揭秘英偉達 GPU 架構演進近十年,從費米到安培

一些特殊的 CPU 也可以透過 NVLink 與 GPU 連線,比如 IBM 的 POWER8。

Volta

2017 年 NVIDIA 釋出了 Volta 架構,這個架構可以說是完全以 Deep Learning 為核心了,相比 Pascal 也是一個大版本。 首先還是一如既往地增加了 SM/Core, 我們就直接看單個 SM 內部吧。

揭秘英偉達 GPU 架構演進近十年,從費米到安培

和 Pascal 的改變類似,到了 Volta,直接拆了 4 個區塊,每個區塊多配了一個 L0 指令快取,而 Shared Memory/Register File 這都沒有變少,也就和 Pascal 的改變一樣,單個執行緒可使用的資源更多了。單個區塊還多個兩個名為 Tensor Core 的單元,這就是這個版本的核心了。可以吐槽一下,這個版本又把 L1 和 Shared Memory 合併了。

我們首先看 CUDA Core, 可以看到,原本的 CUDA Core 被拆成了 FP32 Cuda Core 和 INT32 Cuda Core,這意味著可以同時執行 FP32 和 INT32 的操作。

眾所周知,DeepLearning 的計算瓶頸在矩陣乘法,在 BLAS 中稱為 GEMM,TensorCore 就是隻做 GEMM 計算的單元,可以看到,從這裡開始,NVIDIA 從 SIMT 走到了 SIMT+DSA 的混合。

每個 TensorCore 只做如下操作:

D=A*B+C

即:

揭秘英偉達 GPU 架構演進近十年,從費米到安培

其中 A, B, C, D 都是 4x4 的矩陣,且 A 和 B 是 FP16 矩陣,C 和 D 可以是 FP16 或者 FP32。 通常,更大的矩陣計算會被拆解為這樣的 4x4 矩陣乘法。

這樣的矩陣乘法是作為 Thread Warp 級別的操作在 CUDA 9 開始暴露給程式設計師,除此以外,使用 cublas 和 cudnn 當然同樣也會在合適的情況下啟用 TensorCore。

在這個版本中,另一個重要更新是 NVLink, 簡單來說就是更多更快。每個連線提供雙向各自 25GB/s 的頻寬,並且一個 GPU 可以接 6 個 NVLink,而不是 Pascal 時代的 4 個。一個典型的拓撲如下圖:

揭秘英偉達 GPU 架構演進近十年,從費米到安培

從 Volta 開始,執行緒排程發生了變化,在 Pascal 以及之前的 GPU 上,每個 Warp 裡的 32 個執行緒共享一個 Program Counter (簡稱 PC) ,並且使用一個 Active Mask 表示任意時刻哪些執行緒是可執行的,一個經典的執行如下:

揭秘英偉達 GPU 架構演進近十年,從費米到安培

直到第一個分支完整結束,才會執行另一個分支。這意味著同一個 warp 內不同分支失去了併發性,不同分支的執行緒互相無法傳送訊號或者交換資料,但同時,不同 warp 之間的執行緒又保留了併發性,這當中的執行緒併發存在著不一致,事實上如果程式設計師不注意這點,很可能導致死鎖。

在 Volta 中解決了這個問題,同 warp 內的執行緒有獨立的 PC 和棧,如下:

揭秘英偉達 GPU 架構演進近十年,從費米到安培

由於執行時仍然要符合 SIMT,所以存在一個排程最佳化器負責將可執行的執行緒分組,使用 SIMT 模式執行。經典執行如下:

揭秘英偉達 GPU 架構演進近十年,從費米到安培

上圖可以注意到,Z 的執行並沒有被合併,這是因為 Z 可能會產生一些被其他分支需要的資料,所以排程最佳化器只有在確定安全的情況下才會合並 Z,所以上圖 Z 未合併只是一種情況,一般來說,排程最佳化器足夠聰明可以發現安全的合併。程式設計師也可以透過一個 API 來強制合併,如下:

揭秘英偉達 GPU 架構演進近十年,從費米到安培

從 Volta 開始,提高了對多程序併發使用 GPU 的支援。在 Pascal 及之前,多個程序對單一 GPU 的使用是經典的時間片方式。從 Volta 開始,多個用不滿 GPU 的程序可以在 GPU 上並行,如下圖:

揭秘英偉達 GPU 架構演進近十年,從費米到安培

Turing

2018 年 NVIDIA 釋出了 Turing 架構,個人認為是 Volta 的延伸版本,當然首先各種引數加強,不過我們這裡就不提引數加強了。

揭秘英偉達 GPU 架構演進近十年,從費米到安培

比較重要是的增加了一個 RT Core,全名是 Ray Tracing Core, 顧名思義,這個是給遊戲或者模擬用的,因為本人沒有從事過這類工作,就不介紹了。

此外 Turing 裡的 Tensor Core 增加了對 INT8/INT4/Binary 的支援,為了加速 deep learning 的 inference, 這個時候深度學習模型的量化部署也漸漸成熟。

Ampere

2020 年 NVIDIA 釋出了 Ampere 架構,這就是一個大版本了,裡面又細分了 GA100, GA102, GA104, 我們這裡就只關注 GA100。

我們先看 GA100 的 SM:

揭秘英偉達 GPU 架構演進近十年,從費米到安培

這裡面最核心的升級就是 Tensor Core 了。

除了在 Volta 中的 FP16 以及在 Turing 中的 INT8/INT4/Binary,這個版本新加入了 TF32, BF16, FP64 的支援。著重說說 TF32 和 BF16, 如下圖:

揭秘英偉達 GPU 架構演進近十年,從費米到安培

FP16 的問題在於表示範圍不夠大,在梯度計算時容易出現 underflow, 而且前後向計算也相對容易出現 overflow, 相對來說,在深度學習計算裡,範圍比精度要重要得多,於是有了 BF16,犧牲了精度,保持和 FP32 差不多的範圍,在此前比較知名支援 BF16 的就是 TPU。 而 TF32 的設計,在於即汲取了 BF16 的好處,又保持了一定程度對主流 FP32 的相容,FP32 只要截斷就是 TF32 了。先截斷成 TF32 計算,再轉成 FP32, 對歷史的工作幾乎無影響,如下圖:

揭秘英偉達 GPU 架構演進近十年,從費米到安培

另一個變化則是細粒度的結構化稀疏,深度學習模型壓縮這個領域除了量化,稀疏也是一個大方向,只是稀疏化模型難以利用硬體加速,這個版本的 GPU 則為稀疏提供了一些支援,當前的主要目的則是應用於 Inference 場景。

首先說 NVIDIA 定義的稀疏矩陣,這裡稱為 2:4 的結構化稀疏,2:4 的意思是每 4 個元素當中有 2 個值非 0,如下圖:

揭秘英偉達 GPU 架構演進近十年,從費米到安培

首先使用正常的稠密 weight 訓練,訓練到收斂後裁剪到 2:4 的結構化稀疏 Tensor,然後走 fine tune 繼續訓練非 0 的 weight, 之後得到的 2:4 結構化稀疏 weight

理想情況下具有和稠密 weight 一樣的精確度,然後使用此稀疏化後的 weight 進行 Inference。 而這個版本的 TensorCore 支援一個 2:4 的結構化稀疏矩陣與另一個稠密矩陣直接相乘。

最後一個比較重要的特性就是 MIG(Multi-Instance GPU)了,雖然業界的計算規模確實越來越大,但也存在不少的任務因為其特性導致無法用滿 GPU 導致資源浪費,所以存在需求在一個 GPU 上跑多個任務,在這之前有些雲計算廠商會提供虛擬化方案。而在安培中,會為此需求提供支援,稱為 MIG。

可能會有人有疑問,在 Volta 中引入的多程序支援不是解決了問題嗎?舉個例子,在 Volta 中,雖然多個程序可以並行,但是由於所有程序都可以訪問所有的記憶體資源,可能存在一個程序把所有的 DRAM 頻寬佔滿影響到其他程序的執行,而這些被影響的程序很可能有 Throughput/Latency 要求。所以我們需要更嚴格的隔離。

而在安培 MIG 中,每個 A100 可以被分為 7 個 GPU 例項被不同的任務使用。每個例項的 SMs 有獨立的記憶體資源,可以保證每個任務有符合預期的穩定的 Throughput/Latency。 使用者可以將這些虛擬的 GPU 例項當成真實的 GPU 使用。

結語

事實上關於各個架構的細節還有很多,限於篇幅這裡只能簡單概述。有機會後面再分享一些更具體的關於 CUDA 程式設計的東西。也歡迎大家與我多多交流(線上線下都歡迎),共同進步。

原文來源:

https://zhuanlan.zhihu.com/p/413145211

Reference

https://images.nvidia.com/aem-dam/en-zz/Solutions/geforce/ampere/pdf/NVIDIA-ampere-GA102-GPU-Architecture-Whitepaper-V1.pdf

https://images.nvidia.com/aem-dam/en-zz/Solutions/design-visualization/technologies/turing-architecture/NVIDIA-Turing-Architecture-Whitepaper.pdf

https://images.nvidia.com/content/volta-architecture/pdf/volta-architecture-whitepaper.pdf

https://images.nvidia.com/content/pdf/tesla/whitepaper/pascal-architecture-whitepaper.pdf

https://www.microway.com/download/whitepaper/NVIDIA_Maxwell_GM204_Architecture_Whitepaper.pdf

https://www.nvidia.com/content/PDF/product-specifications/GeForce_GTX_680_Whitepaper_FINAL.pdf

http://www.hardwarebg.com/b4k/files/nvidia_gf100_whitepaper.pdf

https://developer.nvidia.com/content/life-triangle-nvidias-logical-pipeline

https://blog.nowcoder.net/n/4dcb2f6a55a34de9ae6c9067ba3d3bfb

https://jcf94.com/2020/05/24/2020-05-24-nvidia-arch/

https://docs.nvidia.com/cuda/cuda-c-programming-guide/index.html

https://en.wikipedia.org/wiki/Bfloat16_floating-point_format