作者:Keepin
1、cuda是英偉達(dá)開(kāi)發(fā)的一套應(yīng)用軟件接口(API)。其主要應(yīng)用于英偉達(dá)GPU顯卡的調(diào)用。
2、云計(jì)算可以簡(jiǎn)單的理解為是通過(guò)網(wǎng)絡(luò)組合成的計(jì)算機(jī)集群,用于各種加速,其中以CPU為主,GPU為輔。所以CUDA可以成為云計(jì)算的一個(gè)支柱。
3、神經(jīng)網(wǎng)絡(luò)能加速的有很多,當(dāng)然使用硬件加速是最可觀的了,而目前除了專用的NPU(神經(jīng)網(wǎng)絡(luò)加速單元),就屬于GPU對(duì)神經(jīng)網(wǎng)絡(luò)加速效果最好了:
3.1、Cuda的整套軟件非常完善,所以早期很多廠家用cuda能快速搭建神經(jīng)網(wǎng)絡(luò)加速軟件框架,所以cuda也被廣泛應(yīng)用于神經(jīng)網(wǎng)絡(luò)加速。(當(dāng)然英偉達(dá)還專門開(kāi)發(fā)了一套神經(jīng)網(wǎng)絡(luò)套件,叫做tensorrt,在神經(jīng)網(wǎng)絡(luò)推理部署的時(shí)候更為廣泛應(yīng)用)
3.2、從開(kāi)發(fā)api來(lái)說(shuō),神經(jīng)網(wǎng)絡(luò)的加速不僅僅可以用cuda加速,還可以用其他API,例如GPU開(kāi)發(fā)API很多,包括opencl,opengl,vulkan都可以在英偉達(dá)的GPU上加速神經(jīng)網(wǎng)絡(luò),只不過(guò)在英偉達(dá)顯卡上整體來(lái)說(shuō)開(kāi)發(fā)復(fù)雜,這些API優(yōu)化效率不如cuda,畢竟cuda是英偉達(dá)自家開(kāi)發(fā)的,自然適配地非常好
3.3、從硬件角度來(lái)說(shuō),AMD的顯卡也可以用于加速神經(jīng)網(wǎng)絡(luò),只不過(guò)軟件生態(tài)做得不如英偉達(dá),自然應(yīng)用程度也不如英偉達(dá)。事實(shí)上,AMD也開(kāi)發(fā)一套Miopen的神經(jīng)網(wǎng)絡(luò)加速套件,類似于tensorrt;還開(kāi)發(fā)了一套rocm套件,類似于cuda,只不過(guò)市場(chǎng)占有率較低而已。
3.4、amd和nvidia都是以桌面端的GPU為主,在移動(dòng)端,例如手機(jī)芯片,高通、蘋果和arm的GPU也可以用于加速神經(jīng)網(wǎng)絡(luò),主要使用opencl,vulkan,metal等開(kāi)發(fā)API來(lái)實(shí)現(xiàn)。
cuda的官方文檔:https://docs.nvidia.com/cuda/
學(xué)習(xí)路線
sazc
入門CUDA
說(shuō)到入門,個(gè)人比較推薦《CUDA C編程權(quán)威指南》,雖然這本書年代比較久,原版書2014年出版的,使用的GPU最新也僅僅是2012年的CC3.X的Kepler,但這些基礎(chǔ)知識(shí)仍然是掌握CUDA最新特性必不可少的,因此還是值得當(dāng)作入門學(xué)習(xí)讀物的。
進(jìn)階CUDA
要想Coding出高質(zhì)量的Kernel,那么官方最新的《CUDA C++ Programming Guide》便是讓我們可以全面了解到CUDA特性的最直接的辦法,目前最新的版本是12.2。
注:此手冊(cè)個(gè)人認(rèn)為適合隨時(shí)查閱,用到啥查啥,從頭開(kāi)始閱讀學(xué)習(xí),會(huì)耗費(fèi)巨大的精力,效果甚微。
CUDA Samples
如果想利用CUDA特性,快速搭建“簡(jiǎn)單”的Kernel原型,那么官方的《CUDA Samples》便是再好不過(guò)的參考資料。
注:筆者曾經(jīng)簡(jiǎn)單過(guò)了一遍這個(gè)repo,后面有任務(wù)需要編寫分類算法、理解CUDA特性、學(xué)習(xí)CUDA 擴(kuò)展庫(kù)等,均從中找到了合適的Demo,快速搭建原型。
CUDA: NEW FEATURES AND BEYOND
如果對(duì)每代CUDA最新特性感興趣,那么每年一次或兩次的官方GTC發(fā)布的《CUDA: NEW FEATURES AND BEYOND》便是可以從宏觀層面快速了解到該特性的最好方法之一。
注:觀看視頻,下載PDF需要注冊(cè),比較老的版本 Google可以搜到下載鏈接。
CUDA Library
cuDNN、cuBlas、cuDLA...。
GPU架構(gòu)
搞CUDA的要不理解一些GPU的特性,那么永遠(yuǎn)寫不出優(yōu)質(zhì)的代碼,學(xué)習(xí)GPU架構(gòu)可以從白皮書、GTC、官方Blog以及論壇的暗示入手。
SASS
官方的Binary Utilities與論壇的暗示、開(kāi)源的Assembler、泄漏的SASS規(guī)格書、工具分析。
性能分析工具
Nsight System、Nsight Compute、Nsight VS Code等。
專業(yè)CUDA
答主目前還沒(méi)到這個(gè)階段,暫且挖坑。
最近有讀者伙伴在問(wèn)我關(guān)于GPU的資料,這里分享三本電子書。僅作臨時(shí)學(xué)習(xí)使用,請(qǐng)購(gòu)買正版書籍,支持原創(chuàng)作者。
公眾號(hào)回復(fù)獲取電子書:GPU
GPU CUDA 編程的基本原理是什么?
作者:董鑫
想學(xué)好 CUDA 編程, 第一步就是要理解 GPU 的硬件結(jié)構(gòu), 說(shuō)到底,CUDA 的作用就是最大程度壓榨出 NVIDIA GPU 的計(jì)算資源.
想要從零理解起來(lái), 還有有些難度. 這里希望能夠用最簡(jiǎn)單的方式把一些最基本的內(nèi)容講清楚. 所以, 本文以易懂性為主, 犧牲了一些完全準(zhǔn)確性.
GPU 結(jié)構(gòu)
這是 GPU 的基本結(jié)構(gòu). CUDA 編程主打一個(gè)多線程 thread.
多個(gè) thread 成為一個(gè) thread block, 而 thread block 就是由這么一個(gè) Streaming Multiprocessor (SM) 來(lái)運(yùn)行的.
CUDA 編程中有一個(gè) "同一個(gè) block 內(nèi)的 thread 共享一段內(nèi)存". 這恰好對(duì)應(yīng)上圖下面的那段 "Shared Memory".
另外, 一個(gè) SM 里面有多個(gè) subcore (本圖中有 4 個(gè)), 每個(gè) subcore 有一個(gè) 32 thread 的 warp scheduler 和 dispatcher, 這個(gè)是跟 CUDA 編程中 warp 的概念有關(guān)的.
另外, 我們還要理解 GPU 的金字塔狀的 Memory 結(jié)構(gòu).
最快的是 register, 但是非常小.
其次是 l1 cache, 也就是前面提到的 shared memory.
Global memory 就是我們常說(shuō)的 顯存 (GPU memory). 其實(shí)是比較慢的.
Global memory 和 shared memory 之間是 L2 cache, L2 cache 比 global memory 快. 每次 shared memory 要到 global memory 找東西的時(shí)候, 會(huì)去看看 l2 cache 里面有沒(méi)有, 有的話就不用去 global memory 了.
有的概率越大, 我們說(shuō) memory hit rate 越高,CUDA 編程的一個(gè)目的也是要盡可能提高 hit rate. 總的來(lái)說(shuō),
我們就是希望能夠盡可能多的利用比較快的 SRAM (shared memory). 但是因?yàn)?SRAM 比較小, 所以基本原則就是: 每次往 SRAM 移動(dòng)數(shù)據(jù)的, 都可能多的用這個(gè)數(shù)據(jù). 避免來(lái)來(lái)回回的移動(dòng)數(shù)據(jù).
其實(shí), 這種 idea 直接促成了最近大火的 FlashAttention. FlashAttention 發(fā)現(xiàn)很多操作計(jì)算量不大, 但是 latency 很高, 那肯定是不符合上述的 "每次往 SRAM 移動(dòng)數(shù)據(jù)的". 怎么解決呢?
Attention 基本上是由 matrix multiplication 和 softmax 構(gòu)成的. 我們已經(jīng)知道了 matrix multiplication 是可以分塊做的, 所以就剩下 softmax 能不能分塊做? softmax 其實(shí)也是可以很簡(jiǎn)單的被分塊做的. 所以就有了 FlashAttention. 關(guān)于 GPU Architecture 更進(jìn)一步細(xì)節(jié)推薦大家看這個(gè):
https://www.youtube.com/watch?v=Na9_2G6niMw&t=1166s
一些跟 CUDA 有關(guān)的庫(kù)
首先先理一下 NVIDIA 那么多庫(kù), 他們之間到底是什么關(guān)系?
這里以三個(gè)庫(kù)為例 CuTLASS , CuBLAS, CuDNN .
從名字看, 他們都是用 CUDA 寫出來(lái)的.總結(jié)起來(lái), 他們之間的區(qū)別就是封裝程度不同, 專注的領(lǐng)域不同.
CuTLASS 和 CuBLAS 都是比較專注于最基本的線性代數(shù)的運(yùn)算的, 比如 矩陣乘法, 矩陣加法, 等等.
而 CuDNN 則是更進(jìn)一步, 將關(guān)注點(diǎn)落在了 Deep Neural Network 上面了. 比如, DNN 中非常常見(jiàn)的 convolution 操作, 是通過(guò) 矩陣乘法 來(lái)實(shí)現(xiàn)的.
那么, CuDNN 做的工作更像是, 完成從 convolution 到 矩陣乘法 的轉(zhuǎn)換, 轉(zhuǎn)換之后, 就可以復(fù)用 CuTLASS 或者 CuBLAS 來(lái)做.
當(dāng)前, DNN 里面不全都是矩陣乘法, 遇到一些特殊的算子, CuDNN 也會(huì)自己實(shí)現(xiàn)一些.而 CuTLASS 和 CuBLAS 之間的區(qū)別, 更像是”傻瓜程度” 不同.
CuBLAS 可以理解成 英偉達(dá) 給你找了一堆傻瓜相機(jī), 每個(gè)相機(jī)都針對(duì)某個(gè)非常非常具體的場(chǎng)景優(yōu)化到了極致, 你唯一要做的就是根據(jù)你的場(chǎng)景找到最適合相機(jī).
因?yàn)槊總€(gè)相機(jī)已經(jīng)被寫死了, 你不知道英偉達(dá)到底做了什么黑魔法優(yōu)化, 你也沒(méi)辦法去根據(jù)想法進(jìn)一步修改.
雖然 CuBLAS 閉源, 不靈活, 但是只要場(chǎng)景找對(duì)了, 效果確實(shí)拔群.
而 CuTLASS 是一個(gè)模板庫(kù), 相當(dāng)于有這么一些不同 系列 的相機(jī), 有拍夜景, 拍近景的, 等等. 當(dāng)然, 就夜景而言, 肯定還有各種不同場(chǎng)景, 對(duì)應(yīng)不同配置可以嘗試. 在 CuTLASS 你可以嘗試不同的配置, 直到你滿意為止.
矩陣乘法 (CuTLASS)
假設(shè)做 GEMM (general matrix multiplication), 我們以 CuTLASS 里面的實(shí)現(xiàn)來(lái)講解
C = A * B
A 是 M x K
B 是 K x N
這張圖的信息量很大.
1. 第一部分:
是關(guān)于如何拆分一個(gè)大矩陣乘法到多個(gè)小矩陣乘法. 也就是說(shuō), 這段時(shí)間我們就 focus on 某個(gè)小矩陣的計(jì)算. 其他部分我們不管.
這樣, 因?yàn)椴煌【仃囍g是完全獨(dú)立的, 就可以很容易實(shí)現(xiàn)并行.
具體來(lái)說(shuō) 也是需要多次紫色和黃色部分的矩陣乘法. 紫色從左到右滑動(dòng), 黃色從上到下滑動(dòng). 他們的滑動(dòng)的次數(shù)是完全一樣.
他們每滑動(dòng)一次, 就是一次矩陣乘法(及加法, 因?yàn)橐殉顺鰜?lái)的結(jié)果加到之前的結(jié)果之上). 下圖是滑動(dòng)到某個(gè)位置的情況.
之所以綠色部分沒(méi)有高亮, 是因?yàn)橐阉卸蓟瑒?dòng)完, 才能得到最終綠色部分的結(jié)果.
2. 第二部分:
先忽略綠色部分.注意, 這紫色的一整行和黃色的一整列都是由一個(gè) thread block 處理的.為什么? 因?yàn)樗麄兌家谕粔K C tile 上面累加.
如果切換 thread block 的話, 就會(huì)比較麻煩.理論上來(lái)說(shuō), 紫色的一整行和黃色的一整列都要被 load 到 shared memory.
但是, 因?yàn)槲覀兠看沃粫?huì)用到 紫色高亮部分 和 黃色高亮部分, 所以完全可以聲明一塊小內(nèi)存, 每次只 load 一小塊 (A tile and B tile), 用完之后就可以覆蓋掉, 用來(lái) load 下一個(gè)滑動(dòng)位置.具體到某一次滑動(dòng),
把 紫色高亮部分 (A tile) 和 黃色高亮部分 (B tile) 從 global memory 移動(dòng)到 shared memory (SMEM).接下來(lái)要做的就是計(jì)算 A tile * B tile, 然后把結(jié)果累加到 C tile 上面.好了, 現(xiàn)在我們明確兩點(diǎn):A tile 和 B tile 都在 shared memory 了.
也就是說(shuō), thread block 里面的每一個(gè) thread 都可以看到 A tile 和 B tile.A tile 和 B tile 每次窗口滑動(dòng)是需要重新 load 的,
但是 C tile 每次窗口滑動(dòng)是需要在原來(lái)的基礎(chǔ)上更新的.因?yàn)?C tile 一直需要更新, 而且這個(gè)更新是計(jì)算驅(qū)動(dòng)的 (不是 load 驅(qū)動(dòng)的),
所以我們最好把 C tile 放到 register (RF) 里面去. register 是一種比 shared memory 更快的內(nèi)存, 而且 register 是跟 thread 相關(guān)的.
其實(shí)計(jì)算的時(shí)候, A tile 和 B tile 也必須從 shared memory 被 load 到 register. 只是 C tile 一直待在 register 里面等待被更新.
3. 第三部分:
剛剛講了, 我們需要把 C tile held 在 register, 而 register 有是跟具體某個(gè) thread 是關(guān)聯(lián)的. 所以我們自然需要根據(jù) thread 再來(lái)劃分 C tile.
但是, CUDA 其實(shí)在 thread 和 thread block 還有一層叫做 warp. 我們可以暫時(shí)理解把 warp 一種計(jì)量單位: 1 warp = 32 thread.在上述例子中, 一個(gè) thread block 被分成了八個(gè) warp.每個(gè) warp 承擔(dān)了 C tile 中的 16*8 個(gè)數(shù)字.
假設(shè)每個(gè)數(shù)字是 32bit, 那么恰好是一個(gè) register 的容量.那么, 也就是說(shuō), 我們需要 16*8 個(gè) registers. 前面講了一個(gè) warp 是 32 thread. 這并不矛盾. 因?yàn)橐粋€(gè) thread 可以配有多個(gè) registers.
但是, 某個(gè) thread 是不定訪問(wèn) 其他 thread 的 register 的. CUDA 之所以這么限制, 主要原因還是 thread 之間做 sync 太復(fù)雜.這里, 要算一個(gè) warp 對(duì)應(yīng)的結(jié)果, 還是跟前面的講的一樣. 橫向滑動(dòng)紫色高亮塊, 縱向滑動(dòng)黃色高亮塊,
每次滑動(dòng), 都是將當(dāng)前高亮塊從 shared memory load 到 register 中, 計(jì)算矩陣乘法 (雖然看著像向量, 但是不一定是向量), 然后累加到 C warp tile 上面. 正如前面所說(shuō), C warp tile 一直都是在 register 上面的.
4. 第四部分:
最終, 還是要落到單個(gè) thread 上面.
這里的 Warp tile 代表這一整個(gè) tile 都是由一個(gè) Warp 完成的.先看左上角灰色的部分, 后面的內(nèi)容也是在這個(gè)灰色的部分.數(shù)一下, 恰好是 32 個(gè)格子.
而我們知道一個(gè) Warp 是 32 個(gè) thread. 也就是說(shuō), 一個(gè) thread 處理一個(gè)灰色的格子. 這 32 個(gè)格子中, 有一個(gè)綠色的格子 (先不要看另外三個(gè)綠色的),
這個(gè)綠色的格子就是我們現(xiàn)在要討論的 32 個(gè) thread 中的某個(gè) thread.實(shí)際上, 這個(gè)綠色的格子里面有 16 個(gè)數(shù), 也就說(shuō)是一個(gè) 44 的迷你矩陣.
也就說(shuō), 一個(gè) thread 要處理這樣一個(gè) 44 的矩陣. 那么, 既然想得到這樣 44 的矩陣, 就需要讀入 41 的紫色部分, 1*4 的黃色部分.
好, 現(xiàn)在我們可以跳出左上角灰色部分了. 我們發(fā)現(xiàn)這個(gè) Warp tile 還有剩下三個(gè)部分, 但是在左上角灰色部分我們已經(jīng)這個(gè) Warp 中 32 個(gè) thread 用過(guò)一遍了, 怎么辦?
誰(shuí)說(shuō) Warp 只能用一遍? 完全可以讓這個(gè) Warp keep alive, 再如法炮制地去分別按順序處理 (所以這里其實(shí)在時(shí)間上不是并行的) 剩下的三個(gè)部分.這個(gè)時(shí)候, 我們?cè)倏匆粋€(gè)thread的工作量.
首先, 一個(gè) thread 要被用 4 次, 然后每次要處理 4x4 的迷你矩陣,
所以總的來(lái)看, 一個(gè) thread 相當(dāng)于處理了一個(gè) 8x8 的矩陣. 這就是上圖右邊想表達(dá)的意思.
審核編輯:黃飛
-
神經(jīng)網(wǎng)絡(luò)
+關(guān)注
關(guān)注
42文章
4783瀏覽量
101240 -
gpu
+關(guān)注
關(guān)注
28文章
4789瀏覽量
129437 -
sram
+關(guān)注
關(guān)注
6文章
769瀏覽量
114919 -
多線程
+關(guān)注
關(guān)注
0文章
278瀏覽量
20077 -
CUDA
+關(guān)注
關(guān)注
0文章
121瀏覽量
13698
原文標(biāo)題:GPU與CUDA的一點(diǎn)資料知識(shí)整理
文章出處:【微信號(hào):vision263com,微信公眾號(hào):新機(jī)器視覺(jué)】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
在K520上能使用兩個(gè)GPU進(jìn)行CUDA作業(yè)嗎
串聯(lián)諧振逆變器的基本原理
電機(jī)轉(zhuǎn)動(dòng)的基本原理是什么?
線性電源的基本原理是什么
無(wú)線充電的基本原理是什么
可編程控制器基本原理及應(yīng)用
![可<b class='flag-5'>編程</b>控制器<b class='flag-5'>基本原理</b>及應(yīng)用](https://file1.elecfans.com//web2/M00/A5/05/wKgZomUMNpOAb0bRAACTCww1qlc415.jpg)
CUDA簡(jiǎn)介: CUDA編程模型概述
![<b class='flag-5'>CUDA</b>簡(jiǎn)介: <b class='flag-5'>CUDA</b><b class='flag-5'>編程</b>模型概述](https://file.elecfans.com//web2/M00/3E/3B/poYBAGJfz1SAc0eUAAAzEbPU_94657.png)
評(píng)論