[分享] CUDA 程式設計(10) -- 速成篇(上) - PPT 短網址
文章推薦指數: 80 %
(2) 兩者皆有【中央處理器】,主機上為CPU,裝置上為GPU,指令集不同: 主機上的程式碼使用傳統C/C++ 語法撰寫成,實作與呼叫和一般函式無異, 裝置上的程式碼稱 ...
------文章開始------
作者:a5000ml(咖啡裡的海洋藍)看板:VideoCard
標題:[分享]CUDA程式設計(10)--速成篇(上)
時間:WedNov1222:53:252008
(1)有學弟反應CUDA內容有點繁雜,很多概念容易搞混,而且希望多點範例,
所以這兩個禮拜把之前的文稿整理成【新手速成篇】,希望對他們有所幫助.
(2)順便幫國網打個廣告:CUDA中文教學DVD(免費線上版)出現了
請至國網的教育訓練網登入https://edu.nchc.org.tw
詳情請看編號18026一文
※第十章新手速成篇(上)
============================================================================
前言
============================================================================
因為CUDA的一些延伸語法太繁雜,容易讓人混淆(例如記憶體種類就有4~5種,
同樣的globalmemory又有兩種寫法),所以針對這個問題,寫成了速成篇,
去除那些枝枝節節,只講最重要的,並佐以範例,務求讓初學者【七招闖天下】。
第一招主機、裝置
第二招使用API(配置裝置記憶體&主機和裝置間資料搬移)
第三招函式&呼叫(主機、裝置)
第四招網格、區塊、執行緒(線程群組)
第五招記憶體(主機、裝置、共享)
第六招執行緒同步(網格、區塊)
第七招合併讀取(最佳化)
函式部份,只介紹__global__標籤,記憶體部份,只介紹__shared__標籤,
配置顯示記憶體以及資料搬移的方式,也只使用一種,簡單來說,這份速成篇
並不是完整的CUDA,只是刪減後的正交子集合,用來突顯主要概念,以及避免
初學者常犯的錯誤,熟悉之後,務必再深入了解其它延伸語法。
============================================================================
第一招主機、裝置
============================================================================
(1)區分主機和裝置的不同:
【主機】就是PC。
【裝置】就是顯示卡。
(2)兩者皆有【中央處理器】,主機上為CPU,裝置上為GPU,指令集不同:
主機上的程式碼使用傳統C/C++語法撰寫成,實作與呼叫和一般函式無異,
裝置上的程式碼稱為【核心】(kernel),需使用CUDA的延伸語法(函式前加
__global__等標籤)來撰寫,並於呼叫時指定執行緒群組大小(詳見第三招)
(3)兩者皆有【各自的記憶體】(DRAM),擁有獨立的定址空間:
主機上的透過malloc()、free()、new、delete等函式配置與釋放,
裝置上的透過cudaMalloc()、cudaFree()等API配置與釋放,
主機和裝置之間的資料搬移,使用cudaMemcpy()這個API(詳見第二招)
(4)因為主機和裝置的不同,C/C++的標準函式庫不能在kernel中直接使用,
例如要秀出計算結果,必需使用cudaMemcpy()先將資料搬移至主機,
再呼叫printf或cout等標準輸出函式。
(5)使用時先在主機記憶體設好資料的初始值,然後傳入裝置記憶體,接著執行核心,
如果可以的話就儘量讓資料保留在裝置中,進行一連串的kernel操作,
避免透過PCI-E搬移造成效能下降,最後再將結果傳回主機中顯示。
============================================================================
第二招使用API(配置裝置記憶體&主機和裝置間資料搬移)
============================================================================
最基本的API有5個
(1)配置裝置記憶體cudaMalloc()[cuda.h]
(2)釋放裝置記憶體cudaFree()[cuda.h]
(3)記憶體複製cudaMemcpy()[cuda.h]
(4)錯誤字串解譯cudaGetErrorString()[cuda.h]
(5)同步化cudaThreadSynchronize()[cuda.h]
用法如下
--------------------------------------------------------
(1)配置顯示記憶體cudaMalloc()[cuda.h]
--------------------------------------------------------
cudaError_tcudaMalloc(void**ptr,size_tcount);
ptr指向目的指位器之位址
count欲配置的大小(單位bytes)
傳回值cudaError_t是個enum,執行成功時傳回0,其它的錯誤代號可用
cudaGetErrorString()來解譯.
--------------------------------------------------------
(2)釋放顯示記憶體cudaFree()[cuda.h]
--------------------------------------------------------
cudaError_tcudaFree(void*ptr);
ptr指向欲釋放的位址(devicememory)
--------------------------------------------------------
(3)記憶體複製cudaMemcpy()[cuda.h]
--------------------------------------------------------
cudaError_tcudaMemcpy(void*dst,constvoid*src,size_tcount,
enumcudaMemcpyKindkind);
dst指向目的位址
src指向來源位址
count拷貝區塊大小(單位bytes)
kind有四種拷貝流向
cudaMemcpyHostToHost主機->主機
cudaMemcpyHostToDevice主機->裝置
cudaMemcpyDeviceToHost裝置->主機
cudaMemcpyDeviceToDevice裝置->裝置
--------------------------------------------------------
(4)錯誤字串解譯cudaGetErrorString()[cuda.h]
--------------------------------------------------------
constchar*cudaGetErrorString(cudaError_terror);
傳回錯誤代號(error)所代表的字串
--------------------------------------------------------
(5)同步化cudaThreadSynchronize()[cuda.h]
--------------------------------------------------------
cudaError_tcudaThreadSynchronize(void);
使前後兩個核心時序上分離,確保資料的前後相依性正確
//-------------------------------------------------------------------------
//範例(1):透過裝置記憶體進行複製[081112-api.cu]
//PCI-EPCI-E
//主機記憶體a[]-------->裝置記憶體g[]-------->主機記憶體b[]
//-------------------------------------------------------------------------
#include
呼叫kernel函式的語法比一般C函式多了指定網格和區塊大小的手序:
函式名稱<<>>(函式引數...);
網格和區塊詳見第四招
//-----------------------------------------------------------------------
//範例(2):helloCUDA函式(使用global函式填入字串)[081112-hello.cu]
//-----------------------------------------------------------------------
#include
(2)【執行緒】是最基本的執行單位,程式設計師站在執行緒的角度,透過內建變數,
定出執行緒的位置,對工作進行主動切割。
(3)【區塊】為執行緒的群組,一個區塊可包含1~512個執行緒,
每個執行緒在區塊中擁有唯一的索引編號,記錄於內建變數threadIdx。
每個區塊中包含的執行緒數目,記錄於內建變數blockDim。
相同區塊內的執行緒可同步化,而且可透過共享記憶體交換資料(詳見第五、六招)
(4)【網格】為區塊的群組,一個網格可包含1~65535個區塊,
每個區塊在網格中擁有唯一的索引編號,記錄於內建變數blockIdx。
每個網格中包含的區塊數目,記錄於內建變數gridDim。
網格中的區塊可能會同時或分散在不同時間執行,視硬體情況而定。
(5)內建唯讀變數gridDim,blockDim,blockIdx,threadIdx皆是3D正整數的結構體
uint3gridDim:網格大小(網格中包含的區塊數目)
uint3blockIdx:區塊索引(網格中區塊的索引)
uint3blockDim:區塊大小(區塊中包含的執行緒數目)
uint3threadIdx:執行緒索引(區塊中執行緒的索引)
其中uint3為3D的正整數型態,定義如下
structuint3{
unsignedintx,y,z;
};
這些唯讀變數只能在核心中使用。
(6)核心呼叫時指定的網格和區塊大小對應的就是其中gridDim和blockDim兩變數
uint3gridDim:網格大小(網格中包含的區塊數目)
uint3blockDim:區塊大小(區塊中包含的執行緒數目)
可以在呼叫時只指定一維,此時變數裡面的y和z成員都等於1:
核心名稱<<
(7)網格和區塊大小在設定時有一定的限制
網格:max(gridDim)=65535
區塊:max(blockDim)=512
實際在用的時候blockDim還會有資源上的限制,主要是暫存器數目,
所以有時達不到512這個數量,在3維的情況還會有其它的限制,
建議使用1維的方式呼叫,到核心中再去切,執行緒組態比較簡單,
而且bug和限制也會比較少.
//-----------------------------------------------------------------
//範例(3):列出在各執行緒中看到的區塊和執行緒索引[081112-idx.cu]
//【使用一維結構】
//-----------------------------------------------------------------
#include
延伸文章資訊
- 1NVIDIA CUDA 编程指南
nvcc 的基本工作流程在于从主机代码中分离出设备代码,并且编译设备代码成为一个二进制 ... 包含一个执行配置语法的转换,和进入必要的CUDA Runtime 的起始码(第4.2.3 ...
- 2CUDA · parallel_processing
而在device 上執行的function(device 和global)有一些基本的限制:. 不支援遞迴; 不能有static 變數; 不能使用variable number of argum...
- 3【平行運算】CUDA教學(一) 概念介紹 - 都會阿嬤
都會阿嬤- 現在大部份做深度學習的工程師視CUDA、GPU加速為黑盒子, ... 在CUDA 中,最基本的運算單元是thread,很多thread 組成一個block,很多block 組成一個g...
- 4CUDA程式設計指南閱讀筆記
CUDA基本概念(上) ... 本節將介紹CUDA的一些基本的程式設計概念,該節用到的例子來自於CUDA Sample中的VectorAdd專案。 ... 執行配置語法來設定。
- 5CUDA基本语法 - 简书
示例:向量加的CUDA实现这里通过向量加计算这个例子介绍CUDA的基本知识,主要包括内存操作、核函数(kernel function)以及线程配置等。