cuda學習部落格
本文為本人cuda學習過程中的記錄和理解,多參考@譚升等大佬前輩的部落格,以及NVIDIA官方文件。如有錯誤煩請指正,如有侵權請聯絡刪除。
0. 平行計算與計算機架構
計算機架構是平行計算或者說HPC領域中十分重要的內容。
。。看書後期記得補充。。
無論是OpenMP還是CUDA,寫並行程式主要是分解任務,在軟體層面上大體可以分為‘指令並行’和‘資料並行’。
1. 異構計算與CUDA
cuda基礎
進入正題,我們從hello world開始學習cuda
#include<stdio.h>
__global__ void hello_world(void)
{
printf("hello world!);
}
int main(int argc, char **argv)
{
hello_world<<<1, 10>>>();
cudaDeviceReset();
return 0;
}
下面我們具體審視一下這個簡單的cuda程式。
(1)__global__字首,cuda的精髓,即核函式。核函式的程式碼將在成千上萬個執行緒上執行。
(2)cudaDeviceReset(),實現CPU和Device(即GPU)的同步,保證cpu和gpu一起退出程式。
大家這裡先不用著急,後續我們還會具體談cuda中執行單元的劃分,並NVIDIA為我們提供的若干工具來分析編寫並行程式。
在異構環境中,主機host和裝置device透過pcie匯流排通訊。通訊內容就包括了最重要的記憶體資訊傳遞。CUDA上的記憶體管理API大體和cpu中相同,例如:
cudaMalloc()
諸如此類
跑在匯流排上的一個重要API:cudaMemcpy()
記憶體複製函式,可以實現DeviceToHost/HostTODevice等等過程。
GPU架構
執行緒管理
下面我們進入到執行緒管理。首先,一個核函式只能有一個grid,每個grid可以分為多個block,每個block又可以分為多個thread。CUDA還為同一個block中的執行緒提供了共享記憶體機制,在每個block中單獨劃分出一塊記憶體空間用於共享,透過關鍵詞__shared__訪問,擁有比全域性記憶體更好的效能表現,這點後續我們也會詳談。
核函式
kernel_name<<<grid,block>>>
注意host端核函式執行是非同步的,並且當主機發出執行核函式的指令後,會立即收回控制權。此刻就需要方法來使host和device同步,最常見的是使用cudaMemcpy
來使host等待device資料,從而實現同步。
在CUDA程式編寫時,最常見的是將序列程式中的for迴圈並行化(這點在OpenMP和MPI中已經很熟悉了)
細談執行緒束Warp
CUDA中的同步
無論是pthread中的🔓還是openmp,都有相應的同步機制,CUDA為避免記憶體競爭,也有同步方法。__syncthread()
可以實現同block內的執行緒同步,想要同步不同block的執行緒,方法是藉助核函式執行。
CUDA並行效能分析
這裡我們使用簡單的矩陣加法為例來進行實驗分析,硬體平臺為RTX3060
分析工具介紹
- nvprof:
動態並行
動態並行或者叫他巢狀並行,類似於父程序/子程序的概念。
2.CUDA記憶體
- 暫存器
- 共享記憶體
- 常量記憶體
- 全域性記憶體
- 紋理記憶體
- 本地記憶體
記憶體管理
cudaMelloc() //分配記憶體
cudaMemset() //初始化記憶體
cudaFree() //釋放記憶體
cudaemcpy() //傳輸記憶體
特殊記憶體
固定記憶體:固定記憶體傳輸速更快,但是分配和釋放成本更高
零複製記憶體:
統一記憶體定址:cudaMallocManaged()
記憶體訪問模式
這部分內容至關重要,對於程式調優來說,記得程式碼驗證
1.對齊合併記憶體訪問,減少頻寬浪費
2.足夠的併發記憶體操作,隱藏記憶體延遲